<commit_before>/* global angular */
angular.module('jun.smartScroll', [])

.directive('smartScroll', [
  '$window', '$timeout',
  function ($window, $timeout) {
    'use strict';

    var $ = angular.element,
      EPSILON = 0.9;

    function hasScroll(el) {
      if (el.style) {
        var $el = $(el);
        return isScrollable($el.css('overflow')) || isScrollable($el.css('overflow-x')) || isScrollable($el.css('overflow-y'));
      }
      return false;
    }

    function isScrollable(v) {
      return v === 'scroll' || v === 'auto';
    }

    function getFirstParent(el, fn) {
      while (el) {
        if (fn(el)) {
          return el;
        }
        el = el.parentNode;
      }
      return null;
    }

    function parseDistance(distance, height) {
      if (!distance) {
        return 0;
      }
      if (typeof distance === 'number') {
        return distance;
      }
      if (distance.charAt(distance.length - 1) === '%') {
        return parseInt(distance, 10) / 100 * height;
      }
      return parseInt(distance, 10);
    }

    function getScrollThreshold(distance, viewportLength) {
      return parseDistance(distance, viewportLength) + EPSILON;
    }

    function onScrollTop(opts) {
      var distance = opts.distanceTop;
      if (distance == null) {
        return opts.scrollTop();
      }

      var viewport = opts.viewport,
        remaining = viewport.scrollTop(),
        viewportLength = viewport.innerHeight();
      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        return opts.scrollTop();
      }
    }

    function onScrollLeft(opts) {
      var distance = opts.distanceLeft;
      if (distance == null) {
        return opts.scrollLeft();
      }

      var viewport = opts.viewport,
        remaining = viewport.scrollLeft(),
        viewportLength = viewport.innerWidth();
      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        opts.scrollLeft();
      }
    }

    function getRemainingBottomOrRight(scrollPos, viewportLength, scrollLength) {
      return scrollLength - (scrollPos + viewportLength);
    }

    function getHtml(opts) {
      return opts.html || (opts.html = $('html')[0]);
    }

    function getBody(opts) {
      return opts.body || (opts.body = $('body')[0]);
    }

    function getScrollWidth(el, opts) {
      if (el === $window) {
        return getHtml(opts).scrollWidth || getBody(opts).scrollWidth;
      }
      return el.scrollWidth;
    }

    function getScrollHeight(el, opts) {
      if (el === $window) {
        return getHtml(opts).scrollHeight || getBody(opts).scrollHeight;
      }
      return el.scrollHeight;
    }

    function onScrollBottom(opts) {
      var distance = opts.distanceBottom;
      if (distance == null) {
        return opts.scrollBottom();
      }

      var viewport = opts.viewport,
        scrollPos = viewport.scrollTop(),
        viewportLength = viewport.innerHeight(),
        scrollLength = getScrollHeight(viewport[0], opts),
        remaining = getRemainingBottomOrRight(scrollPos, viewportLength, scrollLength);
      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        return opts.scrollBottom();
      }
    }

    function onScrollRight(opts) {
      var distance = opts.distanceRight;
      if (distance == null) {
        return opts.scrollRight();
      }

      var viewport = opts.viewport,
        scrollPos = viewport.scrollLeft(),
        viewportLength = viewport.innerWidth(),
        scrollLength = getScrollWidth(viewport[0], opts),
        remaining = getRemainingBottomOrRight(scrollPos, viewportLength, scrollLength);
      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        return opts.scrollRight();
      }
    }

    function getOnScroll(scope) {
      return function () {
        var opts = scope.scrollOptions;
        if (opts.disabled) {
          return;
        }

        if (!opts.disabledTop && opts.scrollTop) {
          onScrollTop(opts);
        }
        if (!opts.disabledBottom && opts.scrollBottom) {
          onScrollBottom(opts);
        }
        if (!opts.disabledLeft && opts.scrollLeft) {
          onScrollLeft(opts);
        }
        if (!opts.disabledRight && opts.scrollRight) {
          onScrollRight(opts);
        }
        if (opts.scroll) {
          opts.scroll();
        }
      };
    }

    function getViewport(opts, elem) {
      var viewport = opts.viewport || getFirstParent(elem[0], hasScroll) || $window;
      if (typeof viewport.on !== 'function') {
        viewport = $(viewport);
      }
      return viewport;
    }

    return {
      link: function (scope, elem /*, attrs*/ ) {
        var opts = scope.scrollOptions || {};
        var viewport = opts.viewport = getViewport(opts, elem);
        var onScroll = getOnScroll(scope);

        viewport.on('scroll', onScroll);
        viewport.on('resize', onScroll);

        scope.$on('$destroy', function () {
          viewport.off('scroll', onScroll);
          viewport.off('resize', onScroll);
        });

        $timeout(onScroll);
      }
    };
  }
]);
<commit_msg>update<commit_after>/* global angular */
angular.module('jun.smartScroll', [])

.directive('smartScroll', [
  '$window', '$timeout',
  function ($window, $timeout) {
    'use strict';

    var $ = angular.element,
      EPSILON = 0.9;

    function hasScroll(el) {
      if (el.style) {
        var $el = $(el);
        return isScrollable($el.css('overflow')) || isScrollable($el.css('overflow-x')) || isScrollable($el.css('overflow-y'));
      }
      return false;
    }

    function isScrollable(v) {
      return v === 'scroll' || v === 'auto';
    }

    function getFirstParent(el, fn) {
      while (el) {
        if (fn(el)) {
          return el;
        }
        el = el.parentNode;
      }
      return null;
    }

    function parseDistance(distance, height) {
      if (!distance) {
        return 0;
      }
      if (typeof distance === 'number') {
        return distance;
      }
      if (distance.charAt(distance.length - 1) === '%') {
        return parseInt(distance, 10) / 100 * height;
      }
      return parseInt(distance, 10);
    }

    function getScrollThreshold(distance, viewportLength) {
      return parseDistance(distance, viewportLength) + EPSILON;
    }

    function isDirectionalScroll(remaining, lastRemaining) {
      return lastRemaining != null && remaining < lastRemaining;
    }

    function onScrollTop(opts) {
      var viewport = opts.viewport,
        remaining = viewport.scrollTop(),
        lastRemaining = opts.remainingTop;

      opts.remainingTop = remaining;

      if (!isDirectionalScroll(remaining, lastRemaining)) {
        return;
      }

      var distance = opts.distanceTop;
      if (distance == null) {
        return opts.onScrollTop();
      }

      var viewportLength = viewport.innerHeight();
      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        return opts.onScrollTop();
      }
    }

    function onScrollLeft(opts) {
      var viewport = opts.viewport,
        remaining = viewport.scrollLeft(),
        lastRemaining = opts.remainingLeft;

      opts.remainingLeft = remaining;

      if (!isDirectionalScroll(remaining, lastRemaining)) {
        return;
      }

      var distance = opts.distanceLeft;
      if (distance == null) {
        return opts.onScrollLeft();
      }

      var viewportLength = viewport.innerWidth();
      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        opts.onScrollLeft();
      }
    }

    function getRemainingBottomOrRight(scrollPos, viewportLength, scrollLength) {
      return scrollLength - (scrollPos + viewportLength);
    }

    function getHtml(opts) {
      return opts.html || (opts.html = $('html')[0]);
    }

    function getBody(opts) {
      return opts.body || (opts.body = $('body')[0]);
    }

    function getScrollWidth(el, opts) {
      if (el === $window) {
        return getHtml(opts).scrollWidth || getBody(opts).scrollWidth;
      }
      return el.scrollWidth;
    }

    function getScrollHeight(el, opts) {
      if (el === $window) {
        return getHtml(opts).scrollHeight || getBody(opts).scrollHeight;
      }
      return el.scrollHeight;
    }

    function onScrollBottom(opts) {
      var viewport = opts.viewport,
        scrollPos = viewport.scrollTop(),
        viewportLength = viewport.innerHeight(),
        scrollLength = getScrollHeight(viewport[0], opts),
        remaining = getRemainingBottomOrRight(scrollPos, viewportLength, scrollLength),
        lastRemaining = opts.remainingBottom;

      opts.remainingBottom = remaining;

      if (!isDirectionalScroll(remaining, lastRemaining)) {
        return;
      }

      var distance = opts.distanceBottom;
      if (distance == null) {
        return opts.onScrollBottom();
      }

      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        return opts.onScrollBottom();
      }
    }

    function onScrollRight(opts) {
      var viewport = opts.viewport,
        scrollPos = viewport.scrollLeft(),
        viewportLength = viewport.innerWidth(),
        scrollLength = getScrollWidth(viewport[0], opts),
        remaining = getRemainingBottomOrRight(scrollPos, viewportLength, scrollLength),
        lastRemaining = opts.remainingRight;

      opts.remainingRight = remaining;

      if (!isDirectionalScroll(remaining, lastRemaining)) {
        return;
      }

      var distance = opts.distanceRight;
      if (distance == null) {
        return opts.onScrollRight();
      }

      if (remaining <= getScrollThreshold(distance, viewportLength)) {
        return opts.onScrollRight();
      }
    }

    function getOnScroll(scope) {
      return function () {
        var opts = scope.scrollOptions;
        if (opts.disabled) {
          return;
        }

        if (!opts.disabledTop && opts.onScrollTop) {
          onScrollTop(opts);
        }
        if (!opts.disabledBottom && opts.onScrollBottom) {
          onScrollBottom(opts);
        }
        if (!opts.disabledLeft && opts.onScrollLeft) {
          onScrollLeft(opts);
        }
        if (!opts.disabledRight && opts.onScrollRight) {
          onScrollRight(opts);
        }
        if (opts.onScroll) {
          opts.onScroll();
        }
      };
    }

    function getViewport(opts, elem) {
      var viewport = opts.viewport || getFirstParent(elem[0], hasScroll) || $window;
      if (typeof viewport.on !== 'function') {
        viewport = $(viewport);
      }
      return viewport;
    }

    return {
      link: function (scope, elem /*, attrs*/ ) {
        var opts = scope.scrollOptions || {};
        var viewport = opts.viewport = getViewport(opts, elem);
        var onScroll = getOnScroll(scope);

        viewport.on('scroll', onScroll);
        viewport.on('resize', onScroll);

        scope.$on('$destroy', function () {
          viewport.off('scroll', onScroll);
          viewport.off('resize', onScroll);
        });

        $timeout(onScroll);
      }
    };
  }
]);
<|endoftext|>


<commit_before>/**
 * Renders front page from provided profiles.
 */

 /* jshint strict: false, browser: true, globalstrict: true */
 /* global require, module */

'use strict';
var d3 = require('d3');

var JSON_URI = 'profile';

// Flame chart parameters
var HEIGHT_SCALE = 0.95;
var HEIGHT = window.innerHeight * HEIGHT_SCALE;
var HEIGHT_OFFSET = 100;
var WIDTH = window.innerWidth / 2;
var ZOOM_DURATION = 250;
var TOOLTIP_X = 0;
var TOOLTIP_HEIGHT = 100;
var TOOLTIP_Y = HEIGHT - TOOLTIP_HEIGHT + 10;
var TOOLTIP_DY = 15;

/** Returns full node name. */
function getNodeName(d) {
  return d.module_name + '.' + d.func_name + '@' + d.lineno.toString();
}

/** Flattens stats object. */
function flattenStats(stats) {

  function processNode(node) {
    var curr_node = {};
    for (var stat in node) {
      if (node.hasOwnProperty(stat) && stat != 'children') {
        curr_node[stat] = node[stat];
      }
    }
    results.push(curr_node);
    if (!node.hasOwnProperty('children')) {
      return;
    }
    node.children.forEach(function(child) { processNode(child); });
  }

  var results = [];
  processNode(stats);
  return results;
}

/** Renders profile stats. */
function renderTable(data) {
  var columns = [
    {
      head: 'Function name',
      cl: 'title',
      text: function(row) { return row.func_name; }
    }, {
      head: 'Location',
      cl: 'title',
      text: function(row) { return row.module_name + ' @ ' + row.lineno.toString(); }
    }, {
      head: 'Time %',
      cl: 'num',
      text: function(row) {
        var percent = row.cum_time / data.run_time;
        return 100 * Math.round(percent * 1000) / 1000;
      }
    }, {
      head: 'Cum. time',
      cl: 'num',
      text: function(row) { return row.cum_time; }
    }, {
      head: 'Time per call',
      cl: 'num',
      text: function(row) { return row.time_per_call; }
    }, {
      head: 'Total calls',
      cl: 'num',
      text: function(row) { return row.total_calls; }
    }, {
      head: 'Primitive calls',
      cl: 'num',
      text: function(row) { return row.prim_calls; }},
  ];

  var prof_stats = d3.select('body')
    .append('div')
    .attr('class', 'profile-stats');

  var summary = prof_stats.append('summary');
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Program name: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.program_name);
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Total runtime: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.run_time + ' s');
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Total calls: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.total_calls);
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Primitive calls: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.primitive_calls);

  var table = prof_stats.append('table');

  table.append('thead')
   .append('tr')
   .selectAll('th')
   .data(columns)
   .enter()
   .append('th')
   .attr('class', function(col) { return col.cl; })
   .text(function(col) { return col.head; });

  var stats = flattenStats(data.call_stats);

  table.append('tbody')
   .selectAll('tr')
   .data(stats)
   .enter()
   .append('tr')
   .selectAll('td')
   .data(function(row, i) {
      return columns.map(function(c) {
        var cell = {};
        d3.keys(c).forEach(function(k) {
            cell[k] = typeof c[k] == 'function' ? c[k](row,i) : c[k];
        });
        return cell;
      });
   })
   .enter()
   .append('td')
   .text(function(d) { return d.text; })
   .attr('class', function(d) { return d.cl; });
}

/** Renders profile flame chart tooltip. */
function renderFlameChartTooltip(tooltip_area, d, total_time) {
  var tooltip_text = tooltip_area.append('text');
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('y', TOOLTIP_Y)
    .attr('dy', TOOLTIP_DY)
    .text('Function name: ' + d.func_name);
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Location: ' + d.module_name);
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text(function() {
      var percent = 100 * Math.round(d.cum_time / total_time * 1000) / 1000;
      return 'Time percent: ' + percent + ' %';
   }());
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Cum.time: ' + d.cum_time + ' s');
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Time per call: ' + d.time_per_call + ' s');
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Primitive calls: ' + d.prim_calls);
}

/** Removes profile flame chart from tooltip area. */
function removeFlameChartTooltip(tooltip_area) {
  tooltip_area.selectAll('text').remove();
}

/** Renders profile flame chart. */
function renderFlameChart(data) {
  var color = d3.scale.category10();
  var canvas = d3.select('body')
    .append('div')
    .attr('class', 'chart')
    .append('svg')
    .attr('width', WIDTH)
    .attr('height', HEIGHT);
  var x_scale = d3.scale.linear().range([0, WIDTH]);
  var y_scale = d3.scale.linear().range([0, HEIGHT - TOOLTIP_HEIGHT]);

  var tooltip_area = canvas.append('g');
  tooltip_area.append('rect')
    .attr('x', TOOLTIP_X)
    .attr('y', TOOLTIP_Y)
    .attr('width', WIDTH)
    .attr('height', TOOLTIP_HEIGHT)
    .attr('fill', 'white');

  var flame_chart = d3.layout.partition()
    .sort(null)
    .value(function(d) { return d.cum_time; });

  var call_graph = flame_chart.nodes(data.call_stats);
  var cells = canvas.selectAll(".cell")
    .data(call_graph)
    .enter()
    .append('g')
    .attr('class', 'cell');

  var nodes = cells.append('rect')
    .attr('class', 'rect-normal')
    .attr('x', function(d) { return x_scale(d.x); })
    .attr('y', function(d) { return y_scale(1 - d.y - d.dy); })
    .attr('width', function(d) { return x_scale(d.dx); })
    .attr('height', function(d) { return y_scale(d.dy); })
    .style('fill', function(d) { return color(getNodeName(d) + d.depth.toString()); })
    .on('mouseover', function(d) {
      d3.select(this)
        .attr('class', 'rect-highlight');
      renderFlameChartTooltip(tooltip_area, d, data.run_time);
    })
    .on('mouseout', function(d) {
      d3.select(this)
        .attr('class', 'rect-normal');
      removeFlameChartTooltip(tooltip_area);
    });

  nodes.on('click', function(d) {
    x_scale.domain([d.x, d.x + d.dx]);
    y_scale.domain([0, 1 - d.y]).range([0, d.y ? HEIGHT - HEIGHT_OFFSET : HEIGHT]);
    nodes.transition()
      .duration(ZOOM_DURATION)
      .attr('x', function(d) { return x_scale(d.x); })
      .attr('y', function(d) { return y_scale(1 - d.y - d.dy); })
      .attr('width', function(d) { return x_scale(d.x + d.dx) - x_scale(d.x); })
      .attr('height', function(d) { return y_scale(1 - d.y) - y_scale(1 - d.y - d.dy); });
  });
}

/** Renders whole page. */
function renderView() {
  d3.json(JSON_URI, function(data) {
    renderFlameChart(data);
    renderTable(data);
  });
}

module.exports = {
  'getNodeName': getNodeName,
  'flattenStats': flattenStats
};

renderView();<commit_msg>Add flame chart titles.<commit_after>/**
 * Renders front page from provided profiles.
 */

 /* jshint strict: false, browser: true, globalstrict: true */
 /* global require, module */

'use strict';
var d3 = require('d3');

var JSON_URI = 'profile';

// Flame chart parameters
var HEIGHT_SCALE = 0.95;
var HEIGHT = window.innerHeight * HEIGHT_SCALE;
var HEIGHT_OFFSET = 100;
var WIDTH = window.innerWidth / 2;
var ZOOM_DURATION = 250;
var TOOLTIP_X = 0;
var TOOLTIP_HEIGHT = 100;
var TOOLTIP_Y = HEIGHT - TOOLTIP_HEIGHT + 10;
var TOOLTIP_DY = 15;
var TEXT_OFFSET_X = 5;
var TEXT_OFFSET_Y= 14;
var TEXT_CUTOFF = 0.075 * WIDTH;

/** Returns full node name. */
function getNodeName(d) {
  return d.module_name + '.' + d.func_name + '@' + d.lineno.toString();
}

/** Flattens stats object. */
function flattenStats(stats) {

  function processNode(node) {
    var curr_node = {};
    for (var stat in node) {
      if (node.hasOwnProperty(stat) && stat != 'children') {
        curr_node[stat] = node[stat];
      }
    }
    results.push(curr_node);
    if (!node.hasOwnProperty('children')) {
      return;
    }
    node.children.forEach(function(child) { processNode(child); });
  }

  var results = [];
  processNode(stats);
  return results;
}

/** Renders profile stats. */
function renderTable(data) {
  var columns = [
    {
      head: 'Function name',
      cl: 'title',
      text: function(row) { return row.func_name; }
    }, {
      head: 'Location',
      cl: 'title',
      text: function(row) { return row.module_name + ' @ ' + row.lineno.toString(); }
    }, {
      head: 'Time %',
      cl: 'num',
      text: function(row) {
        var percent = row.cum_time / data.run_time;
        return 100 * Math.round(percent * 1000) / 1000;
      }
    }, {
      head: 'Cum. time',
      cl: 'num',
      text: function(row) { return row.cum_time; }
    }, {
      head: 'Time per call',
      cl: 'num',
      text: function(row) { return row.time_per_call; }
    }, {
      head: 'Total calls',
      cl: 'num',
      text: function(row) { return row.total_calls; }
    }, {
      head: 'Primitive calls',
      cl: 'num',
      text: function(row) { return row.prim_calls; }},
  ];

  var prof_stats = d3.select('body')
    .append('div')
    .attr('class', 'profile-stats');

  var summary = prof_stats.append('summary');
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Program name: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.program_name);
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Total runtime: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.run_time + ' s');
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Total calls: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.total_calls);
  summary.append('p')
    .attr('class', 'summary-name')
    .text('Primitive calls: ')
    .append('span')
    .attr('class', 'summary-value')
    .text(data.primitive_calls);

  var table = prof_stats.append('table');

  table.append('thead')
   .append('tr')
   .selectAll('th')
   .data(columns)
   .enter()
   .append('th')
   .attr('class', function(col) { return col.cl; })
   .text(function(col) { return col.head; });

  var stats = flattenStats(data.call_stats);

  table.append('tbody')
   .selectAll('tr')
   .data(stats)
   .enter()
   .append('tr')
   .selectAll('td')
   .data(function(row, i) {
      return columns.map(function(c) {
        var cell = {};
        d3.keys(c).forEach(function(k) {
            cell[k] = typeof c[k] == 'function' ? c[k](row,i) : c[k];
        });
        return cell;
      });
   })
   .enter()
   .append('td')
   .text(function(d) { return d.text; })
   .attr('class', function(d) { return d.cl; });
}

/** Renders profile flame chart tooltip. */
function renderFlameChartTooltip(tooltip_area, d, total_time) {
  var tooltip_text = tooltip_area.append('text');
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('y', TOOLTIP_Y)
    .attr('dy', TOOLTIP_DY)
    .text('Function name: ' + d.func_name);
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Location: ' + d.module_name);
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text(function() {
      var percent = 100 * Math.round(d.cum_time / total_time * 1000) / 1000;
      return 'Time percent: ' + percent + ' %';
   }());
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Cum.time: ' + d.cum_time + ' s');
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Time per call: ' + d.time_per_call + ' s');
  tooltip_text.append('tspan')
    .attr('x', TOOLTIP_X)
    .attr('dy', TOOLTIP_DY)
    .text('Primitive calls: ' + d.prim_calls);
}

/** Removes profile flame chart from tooltip area. */
function removeFlameChartTooltip(tooltip_area) {
  tooltip_area.selectAll('text').remove();
}

/** Renders profile flame chart. */
function renderFlameChart(data) {
  var color = d3.scale.category10();
  var canvas = d3.select('body')
    .append('div')
    .attr('class', 'chart')
    .append('svg')
    .attr('width', WIDTH)
    .attr('height', HEIGHT);

  var tooltip_area = canvas.append('g');
  tooltip_area.append('rect')
    .attr('x', TOOLTIP_X)
    .attr('y', TOOLTIP_Y)
    .attr('width', WIDTH)
    .attr('height', TOOLTIP_HEIGHT)
    .attr('fill', 'white');

  var flame_chart = d3.layout.partition()
    .sort(null)
    .value(function(d) { return d.cum_time; });

  var cells = canvas.selectAll(".cell")
    .data(flame_chart.nodes(data.call_stats))
    .enter()
    .append('g')
    .attr('class', 'cell');

  var x_scale = d3.scale.linear().range([0, WIDTH]);
  var y_scale = d3.scale.linear().range([0, HEIGHT - TOOLTIP_HEIGHT]);
  var nodes = cells.append('rect')
    .attr('class', 'rect-normal')
    .attr('x', function(d) { return x_scale(d.x); })
    .attr('y', function(d) { return y_scale(1 - d.y - d.dy); })
    .attr('width', function(d) { return x_scale(d.dx); })
    .attr('height', function(d) { return y_scale(d.dy); })
    .style('fill', function(d) { return color(getNodeName(d) + d.depth.toString()); })
    .on('mouseover', function(d) {
      d3.select(this)
        .attr('class', 'rect-highlight');
      renderFlameChartTooltip(tooltip_area, d, data.run_time);
    })
    .on('mouseout', function(d) {
      d3.select(this)
        .attr('class', 'rect-normal');
      removeFlameChartTooltip(tooltip_area);
    });

  // Render flame chart headers.
  var titles = cells.append('text')
    .attr('x', function(d) { return x_scale(d.x) + TEXT_OFFSET_X; })
    .attr('y', function(d) { return y_scale(1 - d.y - d.dy) + TEXT_OFFSET_Y; })
    .text(function(d) {
      var nodeWidth = this.previousElementSibling.getAttribute('width');
      return (nodeWidth > TEXT_CUTOFF) ? d.func_name : '';
    });

  // Zoom in
  nodes.on('click', function(d) {
    x_scale.domain([d.x, d.x + d.dx]);
    y_scale.domain([0, 1 - d.y]).range([0, d.y ? HEIGHT - HEIGHT_OFFSET : HEIGHT]);
    nodes.transition()
      .duration(ZOOM_DURATION)
      .attr('x', function(d) { return x_scale(d.x); })
      .attr('y', function(d) { return y_scale(1 - d.y - d.dy); })
      .attr('width', function(d) { return x_scale(d.x + d.dx) - x_scale(d.x); })
      .attr('height', function(d) { return y_scale(1 - d.y) - y_scale(1 - d.y - d.dy); });

    titles.transition()
      .duration(ZOOM_DURATION)
      .attr('x', function(d) { return x_scale(d.x) + TEXT_OFFSET_X; })
      .attr('y', function(d) { return y_scale(1 - d.y - d.dy) + TEXT_OFFSET_Y; })
      .text(function(d) {
        var nodeWidth = this.previousElementSibling.getAttribute('width');
        return (nodeWidth > TEXT_CUTOFF) ? d.func_name : '';
      });
  });
}

/** Renders whole page. */
function renderView() {
  d3.json(JSON_URI, function(data) {
    renderFlameChart(data);
    renderTable(data);
  });
}

module.exports = {
  'getNodeName': getNodeName,
  'flattenStats': flattenStats
};

renderView();<|endoftext|>


<commit_before>"use strict";

var fs = require("fs"),
    build = require("./build.js"),
    version = "debug",
    debug,
    parameters,
    debugParameters,
    sources = {},
    result;

debug = function () {};

// Cheap parameter parsing
process.argv.slice(2).forEach(function (value) {
    if ("-verbose" === value) {
        debug = function () {
            console.log.apply(console, arguments);
        };
    } else {
        version = value;
    }
});

console.log("Generating version '" + version + "'");
debug("\tReading parameters...");
try {
    debugParameters = JSON.parse(fs.readFileSync("debug.json").toString());
    if ("debug" === version) {
        parameters = debugParameters;
    } else {
        parameters = JSON.parse(fs.readFileSync(version + ".json").toString());
    }
    parameters.debugRewriteOptions = debugParameters.rewriteOptions;
} catch (e) {
    console.error("Unknown or invalid version", e);
    process.exit();
}

// Get the list of sources
debug("\tGetting the list of sources...");
require("../src/sources.js");

// Read sources
gpf.sources().every(function (name) {
    if (!name) {
        // end marker
        return false;
    }
    debug("\tReading " + name + "...");
    sources[name] = fs.readFileSync("../src/" + name + ".js").toString();
    return true;
});
debug("\tReading UMD...");
sources.UMD = fs.readFileSync("UMD.js").toString();
debug("\tReading boot...");
sources.boot = fs.readFileSync("../src/boot.js").toString();

function mkDir (path) {
    var parentPath;
    if (!fs.existsSync(path)) {
        parentPath = path.split("/");
        parentPath.pop();
        parentPath = parentPath.join("/");
        if (parentPath) {
            mkDir(parentPath);
        }
        fs.mkdirSync(path);
    }
}

debug("\tCreating working folder...");
parameters.temporaryPath = "../tmp/build/" + version;
mkDir(parameters.temporaryPath);

//Go over sources to create other temporary folders
gpf.sources().every(function (name) {
    if (!name) {
        // end marker
        return false;
    }
    if (name.indexOf("/")) {
        // remove file name
        name = name.split("/");
        name.pop();
        mkDir(parameters.temporaryPath + "/" + name.join("/"));
    }
    return true;
});

try {
    result = build(sources, parameters, debug);
} catch (e) {
    console.error(e.message);
    if (e.step) {
        console.error("Step: " + e.step);
    }
    if (e.sourceName) {
        console.error("Source name: " + e.sourceName);
    }
    process.exit();
}

debug("\tCreating output folder...");
mkDir("../build");
fs.writeFileSync("../build/gpf-" + version + ".js", result);
<commit_msg>Uses sources.json<commit_after>"use strict";

var fs = require("fs"),
    build = require("./build.js"),
    version = "debug",
    debug,
    parameters,
    debugParameters,
    sources,
    sourcesDict = {},
    result;

debug = function () {};

// Cheap parameter parsing
process.argv.slice(2).forEach(function (value) {
    if ("-verbose" === value) {
        debug = function () {
            console.log.apply(console, arguments);
        };
    } else {
        version = value;
    }
});

console.log("Generating version '" + version + "'");
debug("\tReading parameters...");
try {
    debugParameters = JSON.parse(fs.readFileSync("debug.json").toString());
    if ("debug" === version) {
        parameters = debugParameters;
    } else {
        parameters = JSON.parse(fs.readFileSync(version + ".json").toString());
    }
    parameters.debugRewriteOptions = debugParameters.rewriteOptions;
} catch (e) {
    console.error("Unknown or invalid version", e);
    process.exit();
}

// Get the list of sources
debug("\tGetting the list of sources...");
sources = JSON.parse(fs.readFileSync("../src/sources.json"))
    .filter(function (source) {
        return source.load !== false;
    })
    .map(function (source) {
        return source.name;
    });

// Read sources
sources.forEach(function (name) {
    debug("\tReading " + name + "...");
    sourcesDict[name] = fs.readFileSync("../src/" + name + ".js").toString();
});
debug("\tReading UMD...");
sourcesDict.UMD = fs.readFileSync("UMD.js").toString();
debug("\tReading boot...");
sourcesDict.boot = fs.readFileSync("../src/boot.js").toString();

function mkDir (path) {
    var parentPath;
    if (!fs.existsSync(path)) {
        parentPath = path.split("/");
        parentPath.pop();
        parentPath = parentPath.join("/");
        if (parentPath) {
            mkDir(parentPath);
        }
        fs.mkdirSync(path);
    }
}

debug("\tCreating working folder...");
parameters.temporaryPath = "../tmp/build/" + version;
mkDir(parameters.temporaryPath);

//Go over sources to create other temporary folders
sources.forEach(function (name) {
    if (name.indexOf("/")) {
        // remove file name
        name = name.split("/");
        name.pop();
        mkDir(parameters.temporaryPath + "/" + name.join("/"));
    }
});

try {
    result = build(sourcesDict, parameters, debug);
} catch (e) {
    console.error(e.message);
    if (e.step) {
        console.error("Step: " + e.step);
    }
    if (e.sourceName) {
        console.error("Source name: " + e.sourceName);
    }
    process.exit();
}

debug("\tCreating output folder...");
mkDir("../build");
fs.writeFileSync("../build/gpf-" + version + ".js", result);
<|endoftext|>


<commit_before>import collector from '../functions/collector';
import utils from '../../utils/index';

export default function each(data, fn) {
  const funcs = utils.collection.arrayize(data)
    .map(d => fn.bind(null, d));

  const coll = new collector.Functions(funcs);
  return coll.attachKernel();
}
<commit_msg>resolve undefined for simple collections<commit_after>import collector from '../functions/collector';
import utils from '../../utils/index';

export default function each(data, fn) {
  const funcs = utils.collection.arrayize(data)
    .map(d => fn.bind(null, d));

  const coll = new collector.Functions(funcs);
  return coll.attachKernel((v) => undefined);
}
<|endoftext|>


<commit_before>// chai is an assertion library
let chai = require('chai');

// @see http://chaijs.com/api/assert/
let assert = chai.assert;

// register alternative styles
// @see http://chaijs.com/api/bdd/
chai.expect();
chai.should();

// fs for reading test files
let fs = require('fs');
let rewire = require("rewire");

let organismDetails = rewire("../../../app/Resources/client/jsx/helpers/organismDetails.js");

describe('helpers/organismDetails', () => {
    describe('getBestVernacularNameEOL works properly', () => {
        let getBestVernacularNameEOL = organismDetails.__get__('getBestVernacularNameEOL');
        it('function is available', () => {
            assert.equal(typeof getBestVernacularNameEOL, 'function');
        });
    });
});<commit_msg>Add more test cases for getBestVernacularNameEOL<commit_after>// chai is an assertion library
let chai = require('chai');

// @see http://chaijs.com/api/assert/
let assert = chai.assert;

// register alternative styles
// @see http://chaijs.com/api/bdd/
chai.expect();
chai.should();

// fs for reading test files
let fs = require('fs');
let rewire = require("rewire");

let organismDetails = rewire("../../../app/Resources/client/jsx/helpers/organismDetails.js");

describe('helpers/organismDetails', () => {
    describe('getBestVernacularNameEOL works properly', () => {
        let getBestVernacularNameEOL = organismDetails.__get__('getBestVernacularNameEOL');
        it('function is available', () => {
            assert.equal(typeof getBestVernacularNameEOL, 'function');
        });
        it('function returns empty string if neither scientificName nor vernacularNames are given', () => {
            assert.equal(getBestVernacularNameEOL({}), '');
        });
        it('function returns scientificName if no vernacularNames are given', () => {
            assert.equal(getBestVernacularNameEOL({scientificName: 'My Sciname'}), 'My Sciname');
        });
        it('function returns english vernacularName if present', () => {
            assert.equal(getBestVernacularNameEOL({scientificName: 'My Sciname', vernacularNames: [
                {language: 'de', vernacularName: 'Deutscher Name'},
                {language: 'en', vernacularName: 'English Name'}
            ]}), 'English Name');
        });
        it('function returns eolPreferred english vernacularName if present', () => {
            assert.equal(getBestVernacularNameEOL({scientificName: 'My Sciname', vernacularNames: [
                {language: 'de', vernacularName: 'Deutscher Name'},
                {language: 'en', vernacularName: 'English Name'},
                {language: 'en', vernacularName: 'English Preferred Name', eol_preferred: true},
                {language: 'en', vernacularName: 'Another English Name'}
            ]}), 'English Preferred Name');
        });
    });
});<|endoftext|>


<commit_before>import { SERVER_RESPONSE_DELAY } from "../../_support/constants/Timeouts";

describe("Service Table", function() {
  function openDropdown(serviceName) {
    cy.get(".filter-input-text").type(serviceName); // filter to find the correct service
    cy.get(".form-control-group-add-on")
      .eq(-1)
      .click(); // close filter window
    cy.wait(2000); // wait for data to load
    cy.get(".ReactVirtualized__Grid")
      .eq(-1) // bottom right grid
      .scrollTo("right"); // scroll to the actions column
    cy.get(".dropdown").should("not.to.have.length", 1);
    cy.get(".dropdown")
      .eq(1)
      .click();
  }

  function clickDropdownAction(actionText) {
    cy.get(".dropdown-menu-items")
      .contains(actionText)
      .click();
  }

  context("Destroy Action", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-task-healthy",
        nodeHealth: true
      });
      cy.visitUrl({ url: "/services/overview" });

      openDropdown("sleep");
      clickDropdownAction("Delete");
    });

    it("disables button during API request", function() {
      cy.route({
        method: "DELETE",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: [],
        delay: SERVER_RESPONSE_DELAY
      });
      cy.get(".modal-small .button-danger").should("have.class", "disabled");
    });
  });

  context("Resume Action", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-service-suspended",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview" });
    });

    it("hides the stop option in the service action dropdown", function() {
      openDropdown("sleep");

      cy.get(".dropdown-menu-items li")
        .contains("Stop")
        .should("not.exist");
    });

    it("shows the resume option in the service action dropdown", function() {
      openDropdown("sleep");

      cy.get(".dropdown-menu-items li")
        .contains("Resume")
        .should("not.have.class", "hidden");
    });

    it("opens the resume dialog", function() {
      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-header")
        .contains("Resume Service")
        .should("have.length", 1);
    });

    it("opens the resume dialog with the instances textbox if the single app instance label does not exist", function() {
      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get('input[name="instances"]').should("have.length", 1);
    });

    it("opens the resume dialog without the instances textbox if the single app instance label exists", function() {
      cy.configureCluster({
        mesos: "1-service-suspended-single-instance",
        nodeHealth: true
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get('input[name="instances"]').should("have.length", 0);
    });

    it("disables button during API request", function() {
      cy.route({
        method: "PUT",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: [],
        delay: SERVER_RESPONSE_DELAY
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary")
        .click()
        .should("have.class", "disabled");
    });

    it("closes dialog on successful API request", function() {
      cy.route({
        method: "PUT",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: []
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary").click();
      cy.get(".modal-body").should("to.have.length", 0);
    });

    it("shows error message on conflict", function() {
      cy.route({
        method: "PUT",
        status: 409,
        url: /marathon\/v2\/apps\/\/sleep/,
        response: {
          message: "App is locked by one or more deployments."
        }
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary").click();
      cy.get(".modal-body .text-danger").should(
        "to.have.text",
        "App is locked by one or more deployments."
      );
    });

    it("shows error message on not authorized", function() {
      cy.route({
        method: "PUT",
        status: 403,
        url: /marathon\/v2\/apps\/\/sleep/,
        response: {
          message: "Not Authorized to perform this action!"
        }
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary").click();
      cy.get(".modal-body .text-danger").should(
        "to.have.text",
        "Not Authorized to perform this action!"
      );
    });

    it("reenables button after faulty request", function() {
      cy.route({
        method: "PUT",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: [],
        delay: SERVER_RESPONSE_DELAY
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary")
        .as("primaryButton")
        .click();
      cy.get("@primaryButton").should("have.class", "disabled");
      cy.get("@primaryButton").should("not.have.class", "disabled");
    });

    it("closes dialog on secondary button click", function() {
      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button")
        .contains("Cancel")
        .click();
      cy.get(".modal-body").should("to.have.length", 0);
    });
  });

  context("SDK Services", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-sdk-service",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview/%2Fservices" });
    });

    it("updates package icon", function() {
      const imageUrl = "foo.png";
      const serviceName = "sdk-sleep-with-image";

      cy.get(".service-table")
        .find(`.icon-image-container img[src="${imageUrl}"]`)
        .should("exist");

      cy.contains(serviceName).click();

      cy.get(".breadcrumb")
        .find(`.icon-image-container img[src="${imageUrl}"]`)
        .should("exist");
    });

    it("opens the destroy dialog", function() {
      openDropdown("sdk-sleep");
      clickDropdownAction("Delete");

      cy.get(".modal-header")
        .contains("Delete Service")
        .should("to.have.length", 1);

      cy.get(".modal-body p").contains("sdk-sleep");
      cy.get(".modal .filter-input-text").should("exist");

      cy.get(".modal button")
        .contains("Cancel")
        .click();

      cy.get(".modal").should("not.exist");
    });

    it("opens the scale dialog", function() {
      openDropdown("sdk-sleep");
      clickDropdownAction("Scale");

      cy.get(".modal-header")
        .contains("Scale Service")
        .should("to.have.length", 1);

      cy.get(".modal pre").contains(
        "dcos test --name=/services/sdk-sleep update start --options=options.json"
      );

      cy.get(".modal button")
        .contains("Close")
        .click();

      cy.get(".modal").should("not.exist");
    });

    it("restart does not exist", function() {
      openDropdown("sdk-sleep");

      cy.get(".dropdown-menu-items")
        .contains("restart")
        .should("not.exist");
    });

    it("stop does not exist", function() {
      openDropdown("sdk-sleep");

      cy.get(".dropdown-menu-items")
        .contains("stop")
        .should("not.exist");
    });

    it("has an 'Open Service'-DropdownItem when DCOS_SERVICE_WEB_PATH-label is set", function() {
      openDropdown("sdk-sleep-with-image");

      cy.get(".dropdown-menu-items")
        .contains("Open Service")
        .should("exist");
    });
  });

  context("SDK Groups", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-sdk-service",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview" });
    });

    it("opens the cannot delete group dialog", function() {
      openDropdown("services");
      clickDropdownAction("Delete");

      cy.get(".modal-header")
        .contains("Delete Group")
        .should("to.have.length", 1);

      cy.get(".modal-body").contains(
        "This group needs to be empty to delete it. Please delete any services or groups in the group first."
      );
      cy.get(".modal .filter-input-text").should("not.exist");

      cy.get(".modal button")
        .contains("OK")
        .click();

      cy.get(".modal").should("not.exist");
    });

    it("opens the scale dialog", function() {
      openDropdown("services");
      clickDropdownAction("Scale");

      cy.get(".modal-header")
        .contains("Scale Group")
        .should("to.have.length", 1);

      cy.get(".modal pre").contains(
        "dcos test --name=/services/sdk-sleep update start --options=options.json"
      );

      cy.get(".modal pre").contains(
        "dcos marathon app update /services/sleep options.json"
      );

      cy.get(".modal button")
        .contains("Close")
        .click();

      cy.get(".modal").should("not.exist");
    });
  });
  context("Region Column", function() {
    context("Single Region", () => {
      beforeEach(function() {
        cy.configureCluster({
          mesos: "1-task-healthy-with-region",
          regions: 1,
          nodeHealth: true
        });
        cy.visitUrl({ url: "/services/overview" });
      });

      it("does contain the right region", function() {
        cy.get(".filter-input-text").type("sleep");
        cy.get(".form-control-group-add-on")
          .eq(-1)
          .click(); // close filter window

        cy.get(".ReactVirtualized__Grid")
          .eq(-1) // bottom right grid
          .contains("Region-A")
          .should("to.have.length", 1);
      });
    });

    context("Multiple Regions", () => {
      beforeEach(function() {
        cy.configureCluster({
          mesos: "1-task-healthy-with-region",
          regions: 2,
          nodeHealth: true
        });
        cy.visitUrl({ url: "/services/overview" });
      });

      it("does contain the right region", function() {
        cy.get(".filter-input-text").type("sleep");
        cy.get(".form-control-group-add-on")
          .eq(-1)
          .click(); // close filter window

        cy.get(".ReactVirtualized__Grid")
          .eq(-1) // bottom right grid
          .contains("Region-A, Region-B")
          .should("to.have.length", 1);
      });
    });
  });

  context("Groups", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-sdk-service",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview" });
    });

    it("shows service status counts in group tooltip", function() {
      cy.get('.service-status-icon-wrapper > .tooltip-wrapper')
        .eq(1)
        .trigger("mouseover");
      cy.get(".tooltip").contains("3 Running");
    })
  });
});
<commit_msg>chore: add integration test for group aggregate status text<commit_after>import { SERVER_RESPONSE_DELAY } from "../../_support/constants/Timeouts";

describe("Service Table", function() {
  function openDropdown(serviceName) {
    cy.get(".filter-input-text").type(serviceName); // filter to find the correct service
    cy.get(".form-control-group-add-on")
      .eq(-1)
      .click(); // close filter window
    cy.wait(2000); // wait for data to load
    cy.get(".ReactVirtualized__Grid")
      .eq(-1) // bottom right grid
      .scrollTo("right"); // scroll to the actions column
    cy.get(".dropdown").should("not.to.have.length", 1);
    cy.get(".dropdown")
      .eq(1)
      .click();
  }

  function clickDropdownAction(actionText) {
    cy.get(".dropdown-menu-items")
      .contains(actionText)
      .click();
  }

  context("Destroy Action", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-task-healthy",
        nodeHealth: true
      });
      cy.visitUrl({ url: "/services/overview" });

      openDropdown("sleep");
      clickDropdownAction("Delete");
    });

    it("disables button during API request", function() {
      cy.route({
        method: "DELETE",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: [],
        delay: SERVER_RESPONSE_DELAY
      });
      cy.get(".modal-small .button-danger").should("have.class", "disabled");
    });
  });

  context("Resume Action", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-service-suspended",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview" });
    });

    it("hides the stop option in the service action dropdown", function() {
      openDropdown("sleep");

      cy.get(".dropdown-menu-items li")
        .contains("Stop")
        .should("not.exist");
    });

    it("shows the resume option in the service action dropdown", function() {
      openDropdown("sleep");

      cy.get(".dropdown-menu-items li")
        .contains("Resume")
        .should("not.have.class", "hidden");
    });

    it("opens the resume dialog", function() {
      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-header")
        .contains("Resume Service")
        .should("have.length", 1);
    });

    it("opens the resume dialog with the instances textbox if the single app instance label does not exist", function() {
      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get('input[name="instances"]').should("have.length", 1);
    });

    it("opens the resume dialog without the instances textbox if the single app instance label exists", function() {
      cy.configureCluster({
        mesos: "1-service-suspended-single-instance",
        nodeHealth: true
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get('input[name="instances"]').should("have.length", 0);
    });

    it("disables button during API request", function() {
      cy.route({
        method: "PUT",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: [],
        delay: SERVER_RESPONSE_DELAY
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary")
        .click()
        .should("have.class", "disabled");
    });

    it("closes dialog on successful API request", function() {
      cy.route({
        method: "PUT",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: []
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary").click();
      cy.get(".modal-body").should("to.have.length", 0);
    });

    it("shows error message on conflict", function() {
      cy.route({
        method: "PUT",
        status: 409,
        url: /marathon\/v2\/apps\/\/sleep/,
        response: {
          message: "App is locked by one or more deployments."
        }
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary").click();
      cy.get(".modal-body .text-danger").should(
        "to.have.text",
        "App is locked by one or more deployments."
      );
    });

    it("shows error message on not authorized", function() {
      cy.route({
        method: "PUT",
        status: 403,
        url: /marathon\/v2\/apps\/\/sleep/,
        response: {
          message: "Not Authorized to perform this action!"
        }
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary").click();
      cy.get(".modal-body .text-danger").should(
        "to.have.text",
        "Not Authorized to perform this action!"
      );
    });

    it("reenables button after faulty request", function() {
      cy.route({
        method: "PUT",
        url: /marathon\/v2\/apps\/\/sleep/,
        response: [],
        delay: SERVER_RESPONSE_DELAY
      });

      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button-primary")
        .as("primaryButton")
        .click();
      cy.get("@primaryButton").should("have.class", "disabled");
      cy.get("@primaryButton").should("not.have.class", "disabled");
    });

    it("closes dialog on secondary button click", function() {
      openDropdown("sleep");
      clickDropdownAction("Resume");

      cy.get(".modal-footer .button")
        .contains("Cancel")
        .click();
      cy.get(".modal-body").should("to.have.length", 0);
    });
  });

  context("SDK Services", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-sdk-service",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview/%2Fservices" });
    });

    it("updates package icon", function() {
      const imageUrl = "foo.png";
      const serviceName = "sdk-sleep-with-image";

      cy.get(".service-table")
        .find(`.icon-image-container img[src="${imageUrl}"]`)
        .should("exist");

      cy.contains(serviceName).click();

      cy.get(".breadcrumb")
        .find(`.icon-image-container img[src="${imageUrl}"]`)
        .should("exist");
    });

    it("opens the destroy dialog", function() {
      openDropdown("sdk-sleep");
      clickDropdownAction("Delete");

      cy.get(".modal-header")
        .contains("Delete Service")
        .should("to.have.length", 1);

      cy.get(".modal-body p").contains("sdk-sleep");
      cy.get(".modal .filter-input-text").should("exist");

      cy.get(".modal button")
        .contains("Cancel")
        .click();

      cy.get(".modal").should("not.exist");
    });

    it("opens the scale dialog", function() {
      openDropdown("sdk-sleep");
      clickDropdownAction("Scale");

      cy.get(".modal-header")
        .contains("Scale Service")
        .should("to.have.length", 1);

      cy.get(".modal pre").contains(
        "dcos test --name=/services/sdk-sleep update start --options=options.json"
      );

      cy.get(".modal button")
        .contains("Close")
        .click();

      cy.get(".modal").should("not.exist");
    });

    it("restart does not exist", function() {
      openDropdown("sdk-sleep");

      cy.get(".dropdown-menu-items")
        .contains("restart")
        .should("not.exist");
    });

    it("stop does not exist", function() {
      openDropdown("sdk-sleep");

      cy.get(".dropdown-menu-items")
        .contains("stop")
        .should("not.exist");
    });

    it("has an 'Open Service'-DropdownItem when DCOS_SERVICE_WEB_PATH-label is set", function() {
      openDropdown("sdk-sleep-with-image");

      cy.get(".dropdown-menu-items")
        .contains("Open Service")
        .should("exist");
    });
  });

  context("SDK Groups", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-sdk-service",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview" });
    });

    it("opens the cannot delete group dialog", function() {
      openDropdown("services");
      clickDropdownAction("Delete");

      cy.get(".modal-header")
        .contains("Delete Group")
        .should("to.have.length", 1);

      cy.get(".modal-body").contains(
        "This group needs to be empty to delete it. Please delete any services or groups in the group first."
      );
      cy.get(".modal .filter-input-text").should("not.exist");

      cy.get(".modal button")
        .contains("OK")
        .click();

      cy.get(".modal").should("not.exist");
    });

    it("opens the scale dialog", function() {
      openDropdown("services");
      clickDropdownAction("Scale");

      cy.get(".modal-header")
        .contains("Scale Group")
        .should("to.have.length", 1);

      cy.get(".modal pre").contains(
        "dcos test --name=/services/sdk-sleep update start --options=options.json"
      );

      cy.get(".modal pre").contains(
        "dcos marathon app update /services/sleep options.json"
      );

      cy.get(".modal button")
        .contains("Close")
        .click();

      cy.get(".modal").should("not.exist");
    });
  });
  context("Region Column", function() {
    context("Single Region", () => {
      beforeEach(function() {
        cy.configureCluster({
          mesos: "1-task-healthy-with-region",
          regions: 1,
          nodeHealth: true
        });
        cy.visitUrl({ url: "/services/overview" });
      });

      it("does contain the right region", function() {
        cy.get(".filter-input-text").type("sleep");
        cy.get(".form-control-group-add-on")
          .eq(-1)
          .click(); // close filter window

        cy.get(".ReactVirtualized__Grid")
          .eq(-1) // bottom right grid
          .contains("Region-A")
          .should("to.have.length", 1);
      });
    });

    context("Multiple Regions", () => {
      beforeEach(function() {
        cy.configureCluster({
          mesos: "1-task-healthy-with-region",
          regions: 2,
          nodeHealth: true
        });
        cy.visitUrl({ url: "/services/overview" });
      });

      it("does contain the right region", function() {
        cy.get(".filter-input-text").type("sleep");
        cy.get(".form-control-group-add-on")
          .eq(-1)
          .click(); // close filter window

        cy.get(".ReactVirtualized__Grid")
          .eq(-1) // bottom right grid
          .contains("Region-A, Region-B")
          .should("to.have.length", 1);
      });
    });
  });

  context("Groups", function() {
    beforeEach(function() {
      cy.configureCluster({
        mesos: "1-sdk-service",
        nodeHealth: true
      });

      cy.visitUrl({ url: "/services/overview" });
    });

    it("group status is an aggregate of children", function() {
      cy.get('.status-bar-text')
        .eq(1)
        .contains("Running (3 of 3)")
    });

    it("shows service status counts in group tooltip", function() {
      cy.get('.service-status-icon-wrapper > .tooltip-wrapper')
        .eq(1)
        .trigger("mouseover");
      cy.get(".tooltip").contains("3 Running");
    })
  });
});
<|endoftext|>


<commit_before>/**
 * @fileoverview Enforce ARIA state and property values are valid.
 * @author Ethan Cohen
 */

'use strict';

// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------

import rule from '../../../src/rules/valid-aria-proptypes';
import { RuleTester } from 'eslint';

const parserOptions = {
  ecmaVersion: 6,
  ecmaFeatures: {
    jsx: true
  }
};

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

const ruleTester = new RuleTester();

import ariaAttributes from '../../../src/util/ariaAttributes';

const errorMessage = name => ({
  message: `${name} must be of type ${ariaAttributes[name.toUpperCase()].value}.`,
  type: 'JSXAttribute'
});

ruleTester.run('valid-aria-proptypes', rule, {
  valid: [
    // BOOLEAN
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // STRING
    { code: '
', parserOptions },
    { code: '<div aria-label={`Close`} />', parserOptions },
    { code: '
', parserOptions },

    // TRISTATE
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-checked={`mixed`} />', parserOptions },

    // INTEGER
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-level={`123`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // NUMBER
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-valuemax={`123`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // TOKEN
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`ascending`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`descending`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`none`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`other`} />', parserOptions },

    // TOKENLIST
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`additions`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`additions removals`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`additions removals text`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`removals additions text all`} />', parserOptions }
  ],
  invalid: [
    // BOOLEAN
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '<div aria-hidden={`${abc}`} />', errors: [ errorMessage('aria-hidden') ], parserOptions },

    // STRING
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },

    // TRISTATE
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '<div aria-checked={`${abc}`} />', errors: [ errorMessage('aria-checked') ], parserOptions },

    // INTEGER
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '<div aria-level={`abc`} />', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },

    // NUMBER
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '<div aria-valuemax={`abc`} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },

    // TOKEN
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },

    // TOKENLIST
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions }
  ]
});
<commit_msg>Strengthen tests for valid-aria-proptypes.<commit_after>/**
 * @fileoverview Enforce ARIA state and property values are valid.
 * @author Ethan Cohen
 */

'use strict';

// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------

import rule from '../../../src/rules/valid-aria-proptypes';
import { RuleTester } from 'eslint';

const parserOptions = {
  ecmaVersion: 6,
  ecmaFeatures: {
    jsx: true
  }
};

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

const ruleTester = new RuleTester();

import ariaAttributes from '../../../src/util/ariaAttributes';

const errorMessage = name => ({
  message: `${name} must be of type ${ariaAttributes[name.toUpperCase()].value}.`,
  type: 'JSXAttribute'
});

ruleTester.run('valid-aria-proptypes', rule, {
  valid: [
    // BOOLEAN
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // STRING
    { code: '
', parserOptions },
    { code: '<div aria-label={`Close`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // TRISTATE
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-checked={`mixed`} />', parserOptions },

    // INTEGER
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-level={`123`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // NUMBER
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-valuemax={`123`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // TOKEN
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`ascending`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`descending`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`none`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-sort={`other`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },

    // TOKENLIST
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`additions`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`additions removals`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`additions removals text`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions },
    { code: '<div aria-relevant={`removals additions text all`} />', parserOptions },
    { code: '
', parserOptions },
    { code: '
', parserOptions }
  ],
  invalid: [
    // BOOLEAN
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-hidden') ], parserOptions },
    { code: '<div aria-hidden={`${abc}`} />', errors: [ errorMessage('aria-hidden') ], parserOptions },

    // STRING
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-label') ], parserOptions },

    // TRISTATE
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-checked') ], parserOptions },
    { code: '<div aria-checked={`${abc}`} />', errors: [ errorMessage('aria-checked') ], parserOptions },

    // INTEGER
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '<div aria-level={`abc`} />', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-level') ], parserOptions },

    // NUMBER
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '<div aria-valuemax={`abc`} />', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-valuemax') ], parserOptions },

    // TOKEN
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-sort') ], parserOptions },

    // TOKENLIST
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions },
    { code: '
', errors: [ errorMessage('aria-relevant') ], parserOptions }
  ]
});
<|endoftext|>


<commit_before>/* Copyright 2014 Endless Mobile, Inc. */
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const WebKit2 = imports.gi.WebKit2;

GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE;

/**
 * Class: WebviewSwitcherView
 * View that slides webviews on top of one another
 *
 * This class proxies a WebKit2.WebView (or any other object that has
 * "load-changed" and "decide-policy" signals in its interface).
 * It is used to create a "paging" effect in a browser-type environment.
 * Calling <load_uri()> causes a new web view to be created and slide in on top
 * of the previous webview, which is then destroyed in order to save memory.
 *
 * Parent class:
 *    Gtk.Stack
 */
const WebviewSwitcherView = new Lang.Class({
    Name: 'WebviewSwitcherView',
    GTypeName: 'EknWebviewSwitcherView',
    Extends: Gtk.Stack,
    Properties: {
        /**
         * Property: navigate-forwards
         * Whether the view is conceptually navigating forwards
         *
         * Set this property to true if the view should behave like it is
         * navigating forwards; false if it should behave like it is navigating
         * backwards, for example through a "Back" button.
         * This affects how a new page is displayed when it is loaded using
         * load_uri().
         *
         * Flags:
         *    readwrite, construct
         */
        'navigate-forwards': GObject.ParamSpec.boolean('navigate-forwards',
            'Navigate forwards', 'Whether the view is conceptually navigating forwards or backwards',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
            true)
    },
    Signals: {
        /**
         * Event: create-webview
         * Request creation of a web view to put into this view
         *
         * This signal is emitted when this view is about to load a new page.
         * The signal handler should return a Gtk.Widget.
         * If it returns null, then the next handler will be invoked for this
         * signal, and eventually the default handler.
         *
         * The default handler for this signal does the equivalent of
         * > return new WebKit2.WebView();
         *
         * Return type:
         *   Gtk.Widget
         *
         * Flags:
         *   run last
         */
        'create-webview': {
            return_type: Gtk.Widget.$gtype,
            flags: GObject.SignalFlags.RUN_LAST,
            accumulator: GObject.AccumulatorType.FIRST_WINS
        },
        /**
         * Event: decide-navigation-policy
         * Proxy for the *decide-policy* signal of *WebKit2.WebView*
         *
         * This signal is emitted when the webview requests making a navigation
         * decision.
         * Use it to decide when to switch this view to a new page and when to
         * let the webview load a new page normally.
         *
         * For example, if you want to load a new page in the switcher:
         * > switcher.connect('decide-navigation-policy', function (switcher, decision) {
         * >     switcher.load_uri(decision.request.uri);
         * >     decision.ignore();
         * >     return true; // decision was made
         * > });
         * but if you want to let the page load as normal without switching to
         * a new page, simply return *false* to let the default rules apply.
         *
         * The webview's *decide-policy* signal also asks about different kinds
         * of policy decisions, such as whether to download a file or load a
         * resource request, but those are not proxied here.
         * If you want to track those, connect directly to the webview's
         * original signal.
         *
         * Parameters:
         *   decision - a *GObject.Object*; if you are using a web view, then
         *      this will be a *WebKit2.NavigationPolicyDecision*
         *
         * Return type:
         *   boolean
         *
         * Flags:
         *   run last
         */
        'decide-navigation-policy': {
            param_types: [ GObject.TYPE_OBJECT ],
            return_type: GObject.TYPE_BOOLEAN,
            flags: GObject.SignalFlags.RUN_LAST,
            // FIXME (disappointed voice) Oh, GJS...
            // https://bugzilla.gnome.org/show_bug.cgi?id=729363
            // accumulator: GObject.AccumulatorType.TRUE_HANDLED
        },

        /**
         * Event: display-ready
         * When the page is loaded and fully displayed
         *
         * This signal is emitted when a new page has been loaded and all
         * animations for displaying it are complete.
         */
        'display-ready': {}
    },

    MIN_WIDTH: 400,
    MIN_HEIGHT: 400,

    _init: function (props) {
        this._navigate_forwards = null;
        this._active_view = null;

        props = props || {};
        // WebKit WebViews have no minimum size, so we'll ask for one to keep the
        // webview from getting unusable small
        props.width_request = this.MIN_WIDTH;
        props.height_request = this.MIN_HEIGHT;
        this.parent(props);
    },

    // Default handler for create-webview signal
    // FIXME: This is subject to a bug where return values from default handlers
    // are ignored.
    // https://bugzilla.gnome.org/show_bug.cgi?id=729288
    // When the bug is fixed, move the body of _on_create_webview() to
    // on_create_webview(), and remove the workarounds below and in
    // testWebviewSwitcher.js.
    on_create_webview: function () {},
    _on_create_webview: function () {
        return new WebKit2.WebView();
    },

    get navigate_forwards() {
        return this._navigate_forwards;
    },

    set navigate_forwards(value) {
        // cache the value so we don't have to compare to a StackTransitionType
        // constant, which may change
        if (this._navigate_forwards != value) {
            this._navigate_forwards = value;
            this.transition_type = value? Gtk.StackTransitionType.OVER_LEFT :
                Gtk.StackTransitionType.UNDER_RIGHT;
            this.notify('navigate-forwards');
        }
    },

    /**
     * Method: load_uri
     * Load a new page
     *
     * This function emits the <create-webview> signal in order to get a
     * webview, and subsequently loads *uri* in that webview.
     * When *uri* is finished loading, it makes the webview the visible child
     * of this view (using whatever stack transition type has been previously
     * set on the view).
     * When the transition is complete, it removes the old view.
     *
     * Parameters:
     *    uri - the URI to load (string)
     */
    load_uri: function (uri) {
        let view = this.emit('create-webview');
        // FIXME: this should not be necessary when the bug mentioned above
        // _on_create_webview() is fixed.
        if (view === null)
            view = this._on_create_webview();

        // Web views try to be clever and so won't load a page if they're not
        // visible. We are even cleverer, and stick them in an offscreen
        // window so they think they are visible.
        let offscreen = new Gtk.OffscreenWindow();

        let load_id = view.connect('load-changed', function (v, status) {
            if (status === WebKit2.LoadEvent.FINISHED) {
                view.show_all();
                view.reparent(this);
                if(offscreen !== null) {
                    offscreen.destroy();
                    offscreen = null;
                }

                // Show the prepared view and clean up the old view when it is
                // finished showing
                let id = this.connect('notify::transition-running',
                    function () {
                        if (this.transition_running)
                            return;

                        // Make the preparing view the new active view
                        view.disconnect(load_id);
                        if (this._active_view !== null &&
                            this === this._active_view.get_parent())
                            this.remove(this._active_view);
                        this._active_view = view;
                        this.disconnect(id);
                        this.emit('display-ready');
                    }.bind(this));
                // No transition necessary if there was no previous view
                if(this._active_view !== null)
                    this.visible_child = view;
                else {
                    this._active_view = view;
                    this.emit('display-ready');
                }

                view.connect('decide-policy', function (v, decision, type) {
                    if(type === WebKit2.PolicyDecisionType.NAVIGATION_ACTION)
                        return this.emit('decide-navigation-policy', decision);
                    return false;
                }.bind(this));
            }
        }.bind(this));
        offscreen.add(view);
        offscreen.show_all();
        view.load_uri(uri);
    }
});
<commit_msg>Simply animate the page on LOAD_COMMITTED<commit_after>/* Copyright 2014 Endless Mobile, Inc. */
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const WebKit2 = imports.gi.WebKit2;

GObject.ParamFlags.READWRITE = GObject.ParamFlags.READABLE | GObject.ParamFlags.WRITABLE;

/**
 * Class: WebviewSwitcherView
 * View that slides webviews on top of one another
 *
 * This class proxies a WebKit2.WebView (or any other object that has
 * "load-changed" and "decide-policy" signals in its interface).
 * It is used to create a "paging" effect in a browser-type environment.
 * Calling <load_uri()> causes a new web view to be created and slide in on top
 * of the previous webview, which is then destroyed in order to save memory.
 *
 * Parent class:
 *    Gtk.Stack
 */
const WebviewSwitcherView = new Lang.Class({
    Name: 'WebviewSwitcherView',
    GTypeName: 'EknWebviewSwitcherView',
    Extends: Gtk.Stack,
    Properties: {
        /**
         * Property: navigate-forwards
         * Whether the view is conceptually navigating forwards
         *
         * Set this property to true if the view should behave like it is
         * navigating forwards; false if it should behave like it is navigating
         * backwards, for example through a "Back" button.
         * This affects how a new page is displayed when it is loaded using
         * load_uri().
         *
         * Flags:
         *    readwrite, construct
         */
        'navigate-forwards': GObject.ParamSpec.boolean('navigate-forwards',
            'Navigate forwards', 'Whether the view is conceptually navigating forwards or backwards',
            GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT,
            true)
    },
    Signals: {
        /**
         * Event: create-webview
         * Request creation of a web view to put into this view
         *
         * This signal is emitted when this view is about to load a new page.
         * The signal handler should return a Gtk.Widget.
         * If it returns null, then the next handler will be invoked for this
         * signal, and eventually the default handler.
         *
         * The default handler for this signal does the equivalent of
         * > return new WebKit2.WebView();
         *
         * Return type:
         *   Gtk.Widget
         *
         * Flags:
         *   run last
         */
        'create-webview': {
            return_type: Gtk.Widget.$gtype,
            flags: GObject.SignalFlags.RUN_LAST,
            accumulator: GObject.AccumulatorType.FIRST_WINS
        },
        /**
         * Event: decide-navigation-policy
         * Proxy for the *decide-policy* signal of *WebKit2.WebView*
         *
         * This signal is emitted when the webview requests making a navigation
         * decision.
         * Use it to decide when to switch this view to a new page and when to
         * let the webview load a new page normally.
         *
         * For example, if you want to load a new page in the switcher:
         * > switcher.connect('decide-navigation-policy', function (switcher, decision) {
         * >     switcher.load_uri(decision.request.uri);
         * >     decision.ignore();
         * >     return true; // decision was made
         * > });
         * but if you want to let the page load as normal without switching to
         * a new page, simply return *false* to let the default rules apply.
         *
         * The webview's *decide-policy* signal also asks about different kinds
         * of policy decisions, such as whether to download a file or load a
         * resource request, but those are not proxied here.
         * If you want to track those, connect directly to the webview's
         * original signal.
         *
         * Parameters:
         *   decision - a *GObject.Object*; if you are using a web view, then
         *      this will be a *WebKit2.NavigationPolicyDecision*
         *
         * Return type:
         *   boolean
         *
         * Flags:
         *   run last
         */
        'decide-navigation-policy': {
            param_types: [ GObject.TYPE_OBJECT ],
            return_type: GObject.TYPE_BOOLEAN,
            flags: GObject.SignalFlags.RUN_LAST,
            // FIXME (disappointed voice) Oh, GJS...
            // https://bugzilla.gnome.org/show_bug.cgi?id=729363
            // accumulator: GObject.AccumulatorType.TRUE_HANDLED
        },

        /**
         * Event: display-ready
         * When the page is loaded and fully displayed
         *
         * This signal is emitted when a new page has been loaded and all
         * animations for displaying it are complete.
         */
        'display-ready': {}
    },

    MIN_WIDTH: 400,
    MIN_HEIGHT: 400,

    _init: function (props) {
        this._navigate_forwards = null;
        this._active_view = null;

        props = props || {};
        // WebKit WebViews have no minimum size, so we'll ask for one to keep the
        // webview from getting unusable small
        props.width_request = this.MIN_WIDTH;
        props.height_request = this.MIN_HEIGHT;
        this.parent(props);
    },

    // Default handler for create-webview signal
    // FIXME: This is subject to a bug where return values from default handlers
    // are ignored.
    // https://bugzilla.gnome.org/show_bug.cgi?id=729288
    // When the bug is fixed, move the body of _on_create_webview() to
    // on_create_webview(), and remove the workarounds below and in
    // testWebviewSwitcher.js.
    on_create_webview: function () {},
    _on_create_webview: function () {
        return new WebKit2.WebView();
    },

    get navigate_forwards() {
        return this._navigate_forwards;
    },

    set navigate_forwards(value) {
        // cache the value so we don't have to compare to a StackTransitionType
        // constant, which may change
        if (this._navigate_forwards != value) {
            this._navigate_forwards = value;
            this.transition_type = value? Gtk.StackTransitionType.OVER_LEFT :
                Gtk.StackTransitionType.UNDER_RIGHT;
            this.notify('navigate-forwards');
        }
    },

    /**
     * Method: load_uri
     * Load a new page
     *
     * This function emits the <create-webview> signal in order to get a
     * webview, and subsequently loads *uri* in that webview.
     * When *uri* is finished loading, it makes the webview the visible child
     * of this view (using whatever stack transition type has been previously
     * set on the view).
     * When the transition is complete, it removes the old view.
     *
     * Parameters:
     *    uri - the URI to load (string)
     */
    load_uri: function (uri) {
        let view = this.emit('create-webview');
        // FIXME: this should not be necessary when the bug mentioned above
        // _on_create_webview() is fixed.
        if (view === null)
            view = this._on_create_webview();

        // Web views try to be clever and so won't load a page if they're not
        // visible. We are even cleverer, and stick them in an offscreen
        // window so they think they are visible.
        let offscreen = new Gtk.OffscreenWindow();

        let load_id = view.connect('load-changed', function (v, status) {
            if (status === WebKit2.LoadEvent.COMMITTED) {
                view.show_all();
                view.reparent(this);
                if(offscreen !== null) {
                    offscreen.destroy();
                    offscreen = null;
                }

                // Show the prepared view and clean up the old view when it is
                // finished showing
                let id = this.connect('notify::transition-running',
                    function () {
                        if (this.transition_running)
                            return;

                        // Make the preparing view the new active view
                        view.disconnect(load_id);
                        if (this._active_view !== null &&
                            this === this._active_view.get_parent())
                            this.remove(this._active_view);
                        this._active_view = view;
                        this.disconnect(id);
                        this.emit('display-ready');
                    }.bind(this));
                // No transition necessary if there was no previous view
                if(this._active_view !== null)
                    this.visible_child = view;
                else {
                    this._active_view = view;
                    this.emit('display-ready');
                }

                view.connect('decide-policy', function (v, decision, type) {
                    if(type === WebKit2.PolicyDecisionType.NAVIGATION_ACTION)
                        return this.emit('decide-navigation-policy', decision);
                    return false;
                }.bind(this));
            }
        }.bind(this));
        offscreen.add(view);
        offscreen.show_all();
        view.load_uri(uri);
    }
});
<|endoftext|>


<commit_before>define(function(require){
  var fabric = require("fabric"),
      rectOutlinePts = require("utils/rectOutlinePoints"),
      circleOutlinePts = require("utils/circleOutlinePoints"),
      lineOutlinePts = require("utils/lineOutlinePoints");

  return {
    create: function(canvas){
      var vLine = new fabric.PatternBrush(canvas);

      // override
      vLine.getPatternSrc = function(){
        // create a canvas for the pattern
        var patternCanvas = fabric.document.createElement("canvas");
        patternCanvas.width = patternCanvas.height = 10;

        var ctx = patternCanvas.getContext("2d");
        // create the pattern
        ctx.strokeStyle = this.color;
        ctx.lineWidth = 5;
        ctx.beginPath();
        ctx.moveTo(5, 0);
        ctx.lineTo(5, 10);
        ctx.closePath();
        ctx.stroke();
        // return this canvas pattern
        return patternCanvas;
      };

      return vLine;
    },
    // create outline for the specified shape
    createOutline: function(brush, shape, cfg){
      // TODO can we simplify this?
      // TODO parameter checking
      if (shape === "rect") {
        return rectOutlinePts(brush, cfg.x, cfg.y, cfg.width, cfg.height);
      } else if (shape === "circle") {
        return circleOutlinePts(brush, cfg.x, cfg.y, cfg.radius);
      } else if (shape === "line") {
        return lineOutlinePts(brush, cfg.x1, cfg.y1, cfg.x2, cfg.y2);
      }

      return;
    },
    createShapeBrush: function(canvas, cfg){
      var b = this.create(canvas);
      b.width = cfg.brushWidth || 10;
      b.color = cfg.color;

      var outline = this.createOutline(b, cfg.shape, cfg);

      for (var i = outline.length - 1; i >= 0; i--) {
        b._points.push(new fabric.Point(outline[i].x, outline[i].y));
      }

      b._finalizeAndAddPath();
    }
  };
});<commit_msg>migrate verticalLineBrush to use with_outline_helper mixin<commit_after>define(function(require){
  var fabric = require("fabric"),
      compose = require("flight/lib/compose"),
      withOutlineHelper = require("brushes/with_outline_helper");

  var verticalLineBrush = {
    create: function(canvas){
      var vLine = new fabric.PatternBrush(canvas);

      // override
      vLine.getPatternSrc = function(){
        // create a canvas for the pattern
        var patternCanvas = fabric.document.createElement("canvas");
        patternCanvas.width = patternCanvas.height = 10;

        var ctx = patternCanvas.getContext("2d");
        // create the pattern
        ctx.strokeStyle = this.color;
        ctx.lineWidth = 5;
        ctx.beginPath();
        ctx.moveTo(5, 0);
        ctx.lineTo(5, 10);
        ctx.closePath();
        ctx.stroke();
        // return this canvas pattern
        return patternCanvas;
      };

      return vLine;
    },

    createShapeBrush: function(canvas, cfg){
      var b = this.create(canvas);
      b.width = cfg.brushWidth || 10;
      b.color = cfg.color;

      var outline = this.createOutline(b, cfg.shape, cfg);

      for (var i = outline.length - 1; i >= 0; i--) {
        b._points.push(new fabric.Point(outline[i].x, outline[i].y));
      }

      b._finalizeAndAddPath();
    }
  };

  compose.mixin(verticalLineBrush, [withOutlineHelper]);

  return verticalLineBrush;
});<|endoftext|>