<commit_before>/* global window, $ */

window.correlations = (function () {
    /**
     * Handle any errors by logging them.
     */
    function handleError(error) {
        console.error(error.message);
    }

    var correlationData = {};

    function getDataURL(product) {
        if (product === 'Firefox') {
            return 'https://analysis-output.telemetry.mozilla.org/top-signatures-correlations/data/';
        } else if (product === 'FennecAndroid') {
            return 'https://analysis-output.telemetry.mozilla.org/top-fennec-signatures-correlations/data/';
        } else {
            return null;
        }
    }

    function loadChannelsData(product) {
        return Promise.resolve()
        .then(function () {
            if (correlationData[product]) {
                return correlationData[product];
            }

            var dataURL = getDataURL(product);
            if (!dataURL) {
                console.warn('Correlation results unavailable for the "' + product + '" product.');
                return null;
            }

            return fetch(dataURL + 'all.json.gz')
            .then(function (response) {
                return response.json();
            })
            .then(function (totals) {
                correlationData[product] = {
                    'date': totals['date'],
                };

                ['release', 'beta', 'aurora', 'nightly'].forEach(function (ch) {
                    correlationData[product][ch] = {
                        'total': totals[ch],
                        'signatures': {},
                    };
                });
            })
            .then(function () {
                return correlationData[product];
            });
        });
    }

    function loadCorrelationData(signature, channel, product) {
        return loadChannelsData(product)
        .then(function (channelsData) {
            if (!channelsData || signature in channelsData[channel]['signatures']) {
                return;
            }

            var shaObj = new jsSHA('SHA-1', 'TEXT');
            shaObj.update(signature);
            var sha1signature = shaObj.getHash('HEX');

            return fetch(getDataURL(product) + channel + '/' + sha1signature + '.json.gz')
            .then(function (response) {
                return response.json();
            })
            .then(function (data) {
                correlationData[product][channel]['signatures'][signature] = data;
            });
        })
        .catch(handleError)
        .then(function () {
            return correlationData;
        });
    }

    function itemToLabel(item) {
        return Object.getOwnPropertyNames(item)
        .map(function (key) {
            return key + ' = ' + item[key];
        })
        .join(' ∧ ');
    }

    // Convert the number to a user-readable percentage with four digits.
    // We use the same number of digits for any number (e.g. 100.0%, 03.45%) so
    // that the results are aligned, also when copied and pasted.
    function toPercentage(num) {
        var result = (num * 100).toFixed(2);

        if (result === '100.00') {
            return '100.0';
        }

        if (num < 0.1) {
            return '0' + result;
        }

        return result;
    }

    function confidenceInterval(count1, total1, count2, total2) {
        var prop1 = count1 / total1;
        var prop2 = count2 / total2;
        var diff = prop1 - prop2;

        // Wald 95% confidence interval for the difference between the proportions.
        var standard_error = Math.sqrt(prop1 * (1 - prop1) / total1 + prop2 * (1 - prop2) / total2);
        var ci = [diff - 1.96 * standard_error, diff + 1.96 * standard_error];

        // Yates continuity correction for the confidence interval.
        var correction = 0.5 * (1.0 / total1 + 1.0 / total2);

        return [ci[0] - correction, ci[1] + correction];
    }

    function sortCorrelationData(correlationData, total_reference, total_group) {
        return correlationData
        .sort(function (a, b) {
            // Sort by the number of attributes first (results with a smaller number of attributes
            // are easier to read and are often the most interesting ones).
            var rule_a_len = Object.keys(a.item).length;
            var rule_b_len = Object.keys(b.item).length;

            if (rule_a_len < rule_b_len) {
                return -1;
            }

            if (rule_a_len > rule_b_len) {
                return 1;
            }

            // Then, sort by percentage difference between signature and overall (using the lower endpoint
            // of the confidence interval of the difference).
            var ciA = confidenceInterval(a.count_group, total_group, a.count_reference, total_reference);
            var ciB = confidenceInterval(b.count_group, total_group, b.count_reference, total_reference);

            return Math.min(Math.abs(ciB[0]), Math.abs(ciB[1])) - Math.min(Math.abs(ciA[0]), Math.abs(ciA[1]));
        });
    }

    function getResults(signature, channel, product) {
        return loadCorrelationData(signature, channel, product)
        .then(function (data) {

            if (!data[product]) {
                return 'No correlation data was generated for the "' + product + '" product.';
            }

            if (!data[product][channel]['signatures'][signature] || !data[product][channel]['signatures'][signature]['results']) {
                return 'No correlation data was generated for the signature "' + signature + '" on the "' + channel + '" channel, for the "' + product + '" product.';
            }

            var correlationData = data[product][channel]['signatures'][signature]['results'];
            if (correlationData.length === 0) {
                return 'No correlations found for the signature "' + signature + '" on the "' + channel + '" channel, for the "' + product + '" product.';
            }

            var total_reference = data[product][channel].total;
            var total_group = data[product][channel]['signatures'][signature].total;

            var results = sortCorrelationData(correlationData, total_reference, total_group)
            .map(function (line) {
                var percentGroup = toPercentage(line.count_group / total_group);
                var percentRef = toPercentage(line.count_reference / total_reference);
                return '(' + percentGroup + '% in signature vs ' + percentRef + '% overall) ' + itemToLabel(line.item);
            });
            return results;
        })
        .catch(handleError);
    }

    return {
        getCorrelations: getResults,
    };
})();
<commit_msg>Fixes bug 1319068 - Show comments' "top words" in the correlations results (#3599)<commit_after>/* global window, $ */

window.correlations = (function () {
    /**
     * Handle any errors by logging them.
     */
    function handleError(error) {
        console.error(error.message);
    }

    var correlationData = {};

    function getDataURL(product) {
        if (product === 'Firefox') {
            return 'https://analysis-output.telemetry.mozilla.org/top-signatures-correlations/data/';
        } else if (product === 'FennecAndroid') {
            return 'https://analysis-output.telemetry.mozilla.org/top-fennec-signatures-correlations/data/';
        } else {
            return null;
        }
    }

    function loadChannelsData(product) {
        return Promise.resolve()
        .then(function () {
            if (correlationData[product]) {
                return correlationData[product];
            }

            var dataURL = getDataURL(product);
            if (!dataURL) {
                console.warn('Correlation results unavailable for the "' + product + '" product.');
                return null;
            }

            return fetch(dataURL + 'all.json.gz')
            .then(function (response) {
                return response.json();
            })
            .then(function (totals) {
                correlationData[product] = {
                    'date': totals['date'],
                };

                ['release', 'beta', 'aurora', 'nightly'].forEach(function (ch) {
                    correlationData[product][ch] = {
                        'total': totals[ch],
                        'signatures': {},
                    };
                });
            })
            .then(function () {
                return correlationData[product];
            });
        });
    }

    function loadCorrelationData(signature, channel, product) {
        return loadChannelsData(product)
        .then(function (channelsData) {
            if (!channelsData || signature in channelsData[channel]['signatures']) {
                return;
            }

            var shaObj = new jsSHA('SHA-1', 'TEXT');
            shaObj.update(signature);
            var sha1signature = shaObj.getHash('HEX');

            return fetch(getDataURL(product) + channel + '/' + sha1signature + '.json.gz')
            .then(function (response) {
                return response.json();
            })
            .then(function (data) {
                correlationData[product][channel]['signatures'][signature] = data;
            });
        })
        .catch(handleError)
        .then(function () {
            return correlationData;
        });
    }

    function itemToLabel(item) {
        return Object.getOwnPropertyNames(item)
        .map(function (key) {
            return key + ' = ' + item[key];
        })
        .join(' ∧ ');
    }

    // Convert the number to a user-readable percentage with four digits.
    // We use the same number of digits for any number (e.g. 100.0%, 03.45%) so
    // that the results are aligned, also when copied and pasted.
    function toPercentage(num) {
        var result = (num * 100).toFixed(2);

        if (result === '100.00') {
            return '100.0';
        }

        if (num < 0.1) {
            return '0' + result;
        }

        return result;
    }

    function confidenceInterval(count1, total1, count2, total2) {
        var prop1 = count1 / total1;
        var prop2 = count2 / total2;
        var diff = prop1 - prop2;

        // Wald 95% confidence interval for the difference between the proportions.
        var standard_error = Math.sqrt(prop1 * (1 - prop1) / total1 + prop2 * (1 - prop2) / total2);
        var ci = [diff - 1.96 * standard_error, diff + 1.96 * standard_error];

        // Yates continuity correction for the confidence interval.
        var correction = 0.5 * (1.0 / total1 + 1.0 / total2);

        return [ci[0] - correction, ci[1] + correction];
    }

    function sortCorrelationData(correlationData, total_reference, total_group) {
        return correlationData
        .sort(function (a, b) {
            // Sort by the number of attributes first (results with a smaller number of attributes
            // are easier to read and are often the most interesting ones).
            var rule_a_len = Object.keys(a.item).length;
            var rule_b_len = Object.keys(b.item).length;

            if (rule_a_len < rule_b_len) {
                return -1;
            }

            if (rule_a_len > rule_b_len) {
                return 1;
            }

            // Then, sort by percentage difference between signature and overall (using the lower endpoint
            // of the confidence interval of the difference).
            var ciA = confidenceInterval(a.count_group, total_group, a.count_reference, total_reference);
            var ciB = confidenceInterval(b.count_group, total_group, b.count_reference, total_reference);

            return Math.min(Math.abs(ciB[0]), Math.abs(ciB[1])) - Math.min(Math.abs(ciA[0]), Math.abs(ciA[1]));
        });
    }

    function getResults(signature, channel, product) {
        return loadCorrelationData(signature, channel, product)
        .then(function (data) {

            if (!data[product]) {
                return 'No correlation data was generated for the "' + product + '" product.';
            }

            var signatureData = data[product][channel]['signatures'][signature];

            if (!signatureData || !signatureData.results) {
                return 'No correlation data was generated for the signature "' + signature + '" on the "' + channel + '" channel, for the "' + product + '" product.';
            }

            var correlationData = signatureData.results;
            if (correlationData.length === 0) {
                return 'No correlations found for the signature "' + signature + '" on the "' + channel + '" channel, for the "' + product + '" product.';
            }

            var total_reference = data[product][channel].total;
            var total_group = signatureData.total;

            var results = sortCorrelationData(correlationData, total_reference, total_group)
            .map(function (line) {
                var percentGroup = toPercentage(line.count_group / total_group);
                var percentRef = toPercentage(line.count_reference / total_reference);
                return '(' + percentGroup + '% in signature vs ' + percentRef + '% overall) ' + itemToLabel(line.item);
            });

            results.push('');
            results.push('Top words: ' + signatureData.top_words.join(', '));

            return results;
        })
        .catch(handleError);
    }

    return {
        getCorrelations: getResults,
    };
})();
<|endoftext|>


<commit_before>import _ from 'lodash'
import d3 from 'd3'

import browser from 'components/molecules/charts_d3/utils/browser'
import palettes from 'components/molecules/charts_d3/utils/palettes'

import legend from 'components/molecules/charts_d3/renderer/legend'

var DEFAULTS = {
  aspect: 1,
  domain: _.noop,
  margin: {
    top: 0,
    right: 0,
    bottom: 0,
    left: 0
  },
  data_format: 'pct',
  color: palettes.orange,
  onClick: _.noop,
  yFormat: d => d3.format(Math.abs(d) < 1 ? '.4f' : 'n')(d),
  name: _.property('properties.name'),
  maxBubbleValue: 5000,
  maxBubbleRadius: 25,
  bubbleLegendRatio: [0.1, 0.5, 1]
}

function MapLegend () {
}

_.extend(MapLegend.prototype, {
  defaults: DEFAULTS,

  initialize: function (el, data, options) {
    options = this._options = _.defaults({}, options, DEFAULTS)
    var margin = options.margin

    var aspect = _.get(options, 'aspect', 1)
    this._width = _.get(options, 'width', el.clientWidth)
    this._height = _.get(options, 'height', this._width / aspect)

    var svg = this._svg = d3.select(el).append('svg')
      .attr({
        'class': 'reds',
        'viewBox': '-300 80 ' + this._width + ' ' + this._height
      })

    if (browser.isIE() || browser.isWkhtmlToPdf()) {
      svg.attr({
        'width': this._width,
        'height': this._height
      })
    }

    var g = svg.append('g')
      .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')

    g.append('g').attr('class', 'legend')
    svg.append('g').attr('class', 'bubbles')
    svg.select('.bubbles').append('g').attr('class', 'legend')
    svg.append('g').attr('class', 'stripes')
    svg.select('.stripes').append('g').attr('class', 'legend')

    const lineWidth = 10
    const lineHeight = 10
    const lineInterval = 5

    const defs = svg.append('defs')

    const pattern = defs.append('pattern')
      .attr({
        'id': 'stripe',
        'patternUnits': 'userSpaceOnUse',
        'width': lineWidth,
        'height': lineHeight
      })

    pattern.append('line').attr({'x1': 0, 'y1': 0, 'x2': -lineInterval, 'y2': lineHeight})
    pattern.append('line').attr({'x1': lineInterval, 'y1': 0, 'x2': 0, 'y2': lineHeight})
    pattern.append('line').attr({'x1': 2 * lineInterval, 'y1': 0, 'x2': lineInterval, 'y2': lineHeight})
    pattern.selectAll('line')
      .style('stroke', '#cccccc')
      .attr({'stroke-linecap': 'square', 'stroke-linejoin': 'miter', 'stroke-width': 1})

    this.update(data)
  },

  update: function (data, options) {
    options = _.assign(this._options, options)

    const svg = this._svg

    // MAP COLOR LEGEND
    // ---------------------------------------------------------------------------
    if (options.value) {
      const features = _.reject(data, 'properties.isBorder')
      let domain = options.domain(features)

      if (!_.isArray(domain)) {
        domain = d3.extent(features, options.value)
        domain[0] = Math.min(domain[0], 0)
      }

      const colors = options.color.concat().reverse()
      const colorScale = d3.scale.quantize()
        .domain(domain)
        .range(colors)
      // const boundsReversed = ind.bad_bound > ind.good_bound
      // const names = boundsReversed ? ['good', 'ok', 'bad'] : ['bad', 'ok', 'good']
      // const extents = boundsReversed ? [ ind.good_bound, ind.bad_bound ] : [ ind.bad_bound, ind.good_bound ]
      var legendText = []
      if (options.data_format === 'bool') {
        legendText[1] = 'No'
        legendText[0] = 'Yes'
      } else if (options.data_format === 'pct') {
        //red/yellow/green pattern for 0, 1, 2
        legendText[2] = "0%-"+domain[0]+"%"
        legendText[1] = domain[0]+"%-"+domain[1]+"%"
        legendText[0] = domain[1]+"%-100%"
      } else {
        //double check actual data with this logic
        legendText[2] = domain[1]+"-100"
        legendText[1] = domain[0]+"-"+domain[1]
        legendText[0] = "0-"+domain[0]
      }

      svg.select('.legend')
      .call(legend().scale(d3.scale.ordinal()
        .domain(legendText)
        .range(colorScale.range())))
      .attr('transform', () => 'translate(2, 0)')
    }

    // MAP STRIPES LEGEND
    // ---------------------------------------------------------------------------
    if (options.stripeValue) {
      const stripeLegendColor = d3.scale.ordinal().range(['#FFFFFF', 'url(#stripe)'])
      const stripeLegendText = options.stripeLegendText
      const stripeLegend = svg.select('.stripes').select('.legend')
        .attr('transform', () => 'translate(' + 2 + ', ' + 0 + ')')
        .selectAll('.series').data(stripeLegendText)
        .enter().append('g')
        .attr('class', 'series')
        .attr('transform', (d, i) => `translate(0, ${i * 15})`)

      stripeLegend.append('rect')
        .attr('width', 11)
        .attr('height', 11)
        .style({
          'fill': stripeLegendColor,
          'stroke': '#cccccc',
          'stroke-width': 1
        })

      stripeLegend.append('text')
        .attr({'x': 16, 'y': 3.5, 'dy': 6})
        .style({'text-anchor': 'start', 'font-size': 12})
        .text(d => d)
    }

    // MAP BUBBLES LEGEND
    // ---------------------------------------------------------------------------
    if (options.bubbleValue) {
      const radius = d3.scale.sqrt().domain([0, options.maxBubbleValue]).range([0, options.maxBubbleRadius])
      const bubbleLegendText = _.map(options.bubbleLegendRatio, d => Math.ceil(d * options.maxBubbleValue, -1))
      const bubbleLegend = svg.select('.bubbles').select('.legend')
        .attr('transform', () => 'translate(2, 0)')
        .selectAll('.series').data(bubbleLegendText)
        .enter().append('g')
        .attr('class', 'series')

      let cx = 2.5 * options.maxBubbleRadius
      let cy = d => 2.5 * options.maxBubbleRadius - radius(d)
      const lineY = d => cy(d) - radius(d)

      bubbleLegend.append('circle')
        .attr('r', d => radius(d))
        .attr({ 'cx': cx, 'cy': cy })
        .style({'opacity': 0.5, 'fill': 'transparent', 'stroke': '#AAAAAA'})

      bubbleLegend.append('line')
        .attr({ x1: 0, y1: lineY, x2: cx, y2: lineY })
        .style('stroke', '#AAAAAA')

      bubbleLegend.append('text')
        .attr('dx', 0)
        .attr('dy', lineY)
        .text(d => d)
        .style('fill', '#AAAAAA')
    }
  }
})

export default MapLegend
<commit_msg>modularize legend ticks and normalize value displayed<commit_after>import _ from 'lodash'
import d3 from 'd3'

import browser from 'components/molecules/charts_d3/utils/browser'
import palettes from 'components/molecules/charts_d3/utils/palettes'

import legend from 'components/molecules/charts_d3/renderer/legend'

var DEFAULTS = {
  aspect: 1,
  domain: _.noop,
  margin: {
    top: 0,
    right: 0,
    bottom: 0,
    left: 0
  },
  data_format: 'pct',
  color: palettes.orange,
  onClick: _.noop,
  yFormat: d => d3.format(Math.abs(d) < 1 ? '.4f' : 'n')(d),
  name: _.property('properties.name'),
  maxBubbleValue: 5000,
  maxBubbleRadius: 25,
  bubbleLegendRatio: [0.1, 0.5, 1]
}

function MapLegend () {
}
_.extend(MapLegend.prototype, {
  defaults: DEFAULTS,

  initialize: function (el, data, options) {
    options = this._options = _.defaults({}, options, DEFAULTS)
    var margin = options.margin

    var aspect = _.get(options, 'aspect', 1)
    this._width = _.get(options, 'width', el.clientWidth)
    this._height = _.get(options, 'height', this._width / aspect)

    var svg = this._svg = d3.select(el).append('svg')
      .attr({
        'class': 'reds',
        'viewBox': '-300 80 ' + this._width + ' ' + this._height
      })

    if (browser.isIE() || browser.isWkhtmlToPdf()) {
      svg.attr({
        'width': this._width,
        'height': this._height
      })
    }

    var g = svg.append('g')
      .attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')')

    g.append('g').attr('class', 'legend')
    svg.append('g').attr('class', 'bubbles')
    svg.select('.bubbles').append('g').attr('class', 'legend')
    svg.append('g').attr('class', 'stripes')
    svg.select('.stripes').append('g').attr('class', 'legend')

    const lineWidth = 10
    const lineHeight = 10
    const lineInterval = 5

    const defs = svg.append('defs')

    const pattern = defs.append('pattern')
      .attr({
        'id': 'stripe',
        'patternUnits': 'userSpaceOnUse',
        'width': lineWidth,
        'height': lineHeight
      })

    pattern.append('line').attr({'x1': 0, 'y1': 0, 'x2': -lineInterval, 'y2': lineHeight})
    pattern.append('line').attr({'x1': lineInterval, 'y1': 0, 'x2': 0, 'y2': lineHeight})
    pattern.append('line').attr({'x1': 2 * lineInterval, 'y1': 0, 'x2': lineInterval, 'y2': lineHeight})
    pattern.selectAll('line')
      .style('stroke', '#cccccc')
      .attr({'stroke-linecap': 'square', 'stroke-linejoin': 'miter', 'stroke-width': 1})

    this.update(data)
  },
  getTicksFromBounds: function(bounds,options) {
    // const boundsReversed = ind.bad_bound > ind.good_bound
        // const names = boundsReversed ? ['good', 'ok', 'bad'] : ['bad', 'ok', 'good']
        // const extents = boundsReversed ? [ ind.good_bound, ind.bad_bound ] : [ ind.bad_bound, ind.good_bound ]
    var legendText = []
    if (options.data_format === 'bool') {
      legendText[1] = 'No'
      legendText[0] = 'Yes'
    } else if (options.data_format === 'pct') {
      //green/yellow/red pattern for 0, 1, 2
      bounds = bounds.map(bound => {return bound*100})
      legendText[2] = "0%-"+bounds[0]+"%"
      legendText[1] = bounds[0]+"%-"+bounds[1]+"%"
      legendText[0] = bounds[1]+"%-100%"
    } else {
      //double check actual data with this logic
      legendText[2] = bounds[1]+"-100"
      legendText[1] = bounds[0]+"-"+bounds[1]
      legendText[0] = "0-"+bounds[0]
    }
    return legendText;
  },
  update: function(data, options) {
    options = _.assign(this._options, options)

    const svg = this._svg

    // MAP COLOR LEGEND
    // ---------------------------------------------------------------------------
    if (options.value) {
      const features = _.reject(data, 'properties.isBorder')
      let domain = options.domain(features)

      if (!_.isArray(domain)) {
        domain = d3.extent(features, options.value)
        domain[0] = Math.min(domain[0], 0)
      }

      const colors = options.color.concat().reverse()
      const colorScale = d3.scale.quantize()
        .domain(domain)
        .range(colors)
      const legendText = this.getTicksFromBounds(domain,options)
      debugger;
      svg.select('.legend')
      .call(legend().scale(d3.scale.ordinal()
        .domain(legendText)
        .range(colorScale.range())))
      .attr('transform', () => 'translate(2, 0)')
    }

    // MAP STRIPES LEGEND
    // ---------------------------------------------------------------------------
    if (options.stripeValue) {
      const stripeLegendColor = d3.scale.ordinal().range(['#FFFFFF', 'url(#stripe)'])
      const stripeLegendText = options.stripeLegendText
      const stripeLegend = svg.select('.stripes').select('.legend')
        .attr('transform', () => 'translate(' + 2 + ', ' + 0 + ')')
        .selectAll('.series').data(stripeLegendText)
        .enter().append('g')
        .attr('class', 'series')
        .attr('transform', (d, i) => `translate(0, ${i * 15})`)

      stripeLegend.append('rect')
        .attr('width', 11)
        .attr('height', 11)
        .style({
          'fill': stripeLegendColor,
          'stroke': '#cccccc',
          'stroke-width': 1
        })

      stripeLegend.append('text')
        .attr({'x': 16, 'y': 3.5, 'dy': 6})
        .style({'text-anchor': 'start', 'font-size': 12})
        .text(d => d)
    }

    // MAP BUBBLES LEGEND
    // ---------------------------------------------------------------------------
    if (options.bubbleValue) {
      const radius = d3.scale.sqrt().domain([0, options.maxBubbleValue]).range([0, options.maxBubbleRadius])
      const bubbleLegendText = _.map(options.bubbleLegendRatio, d => Math.ceil(d * options.maxBubbleValue, -1))
      const bubbleLegend = svg.select('.bubbles').select('.legend')
        .attr('transform', () => 'translate(2, 0)')
        .selectAll('.series').data(bubbleLegendText)
        .enter().append('g')
        .attr('class', 'series')

      let cx = 2.5 * options.maxBubbleRadius
      let cy = d => 2.5 * options.maxBubbleRadius - radius(d)
      const lineY = d => cy(d) - radius(d)

      bubbleLegend.append('circle')
        .attr('r', d => radius(d))
        .attr({ 'cx': cx, 'cy': cy })
        .style({'opacity': 0.5, 'fill': 'transparent', 'stroke': '#AAAAAA'})

      bubbleLegend.append('line')
        .attr({ x1: 0, y1: lineY, x2: cx, y2: lineY })
        .style('stroke', '#AAAAAA')

      bubbleLegend.append('text')
        .attr('dx', 0)
        .attr('dy', lineY)
        .text(d => d)
        .style('fill', '#AAAAAA')
    }
  }
})

export default MapLegend
<|endoftext|>


<commit_before>/**
 * Created by fsuda on 05.11.2018.
 */

import vocab from "../../service/vocab.js";
import { get } from "../../utils.js";

export function isUsingTemporaryUri(connection) {
  return !!get(connection, "usingTemporaryUri");
}

export function isRequestSent(connection) {
  return get(connection, "state") === vocab.WON.RequestSent;
}

export function isRequestReceived(connection) {
  return get(connection, "state") === vocab.WON.RequestReceived;
}

export function isSuggested(connection) {
  return get(connection, "state") === vocab.WON.Suggested;
}

export function isConnected(connection) {
  return get(connection, "state") === vocab.WON.Connected;
}

export function isClosed(connection) {
  return get(connection, "state") === vocab.WON.Closed;
}

export function isUnread(connection) {
  return !!get(connection, "unread");
}

export function hasSocketUris(connection, socketUri, targetSocketUri) {
  return (
    hasSocketUri(connection, socketUri) &&
    hasTargetSocketUri(connection, targetSocketUri)
  );
}

export function hasSocketUri(connection, socketUri) {
  return (
    !!connection && !!socketUri && get(connection, "socketUri") === socketUri
  );
}

export function hasTargetSocketUri(connection, socketUri) {
  return (
    !!connection &&
    !!socketUri &&
    get(connection, "targetSocketUri") === socketUri
  );
}

/**
 * Creates a label of the participants and suggestions/requests/invites of a given set of groupChatConnections
 * @param groupChatConnections
 */
export function generateGroupChatMembersLabel(groupChatConnections) {
  const groupMembersSize = groupChatConnections.filter(conn =>
    isConnected(conn)
  ).size;
  const suggestedSize = groupChatConnections.filter(conn => isSuggested(conn))
    .size;
  const invitedSize = groupChatConnections.filter(conn => isRequestSent(conn))
    .size;
  const requestedSize = groupChatConnections.filter(conn =>
    isRequestReceived(conn)
  ).size;

  const createPartOfLabel = (size, entitySingular, entityPlural) => {
    if (size == 0) {
      return "No " + entityPlural + " yet";
    }
    if (size == 1) {
      return size + " " + entitySingular;
    } else if (size > 1) {
      return size + " " + entityPlural;
    }
  };

  let groupMembersLabel = createPartOfLabel(
    groupMembersSize,
    "Group Member",
    "Group Members"
  );

  if (invitedSize > 0) {
    if (groupMembersLabel.length > 0) {
      groupMembersLabel += ", ";
    }
    groupMembersLabel += createPartOfLabel(invitedSize, "Invited", "Invited");
  }

  if (requestedSize > 0) {
    if (groupMembersLabel.length > 0) {
      groupMembersLabel += ", ";
    }
    groupMembersLabel += createPartOfLabel(
      requestedSize,
      "Request",
      "Requests"
    );
  }

  if (suggestedSize > 0) {
    if (groupMembersLabel.length > 0) {
      groupMembersLabel += ", ";
    }
    groupMembersLabel += createPartOfLabel(
      suggestedSize,
      "Suggested",
      "Suggested"
    );
  }

  return groupMembersLabel;
}
<commit_msg>adds filter for socketCapacity reactions<commit_after>/**
 * Created by fsuda on 05.11.2018.
 */

import vocab from "../../service/vocab.js";
import { get } from "../../utils.js";

export function isUsingTemporaryUri(connection) {
  return !!get(connection, "usingTemporaryUri");
}

export function isRequestSent(connection) {
  return get(connection, "state") === vocab.WON.RequestSent;
}

export function isRequestReceived(connection) {
  return get(connection, "state") === vocab.WON.RequestReceived;
}

export function isSuggested(connection) {
  return get(connection, "state") === vocab.WON.Suggested;
}

export function isConnected(connection) {
  return get(connection, "state") === vocab.WON.Connected;
}

export function isClosed(connection) {
  return get(connection, "state") === vocab.WON.Closed;
}

export function isUnread(connection) {
  return !!get(connection, "unread");
}

export function hasSocketUris(connection, socketUri, targetSocketUri) {
  return (
    hasSocketUri(connection, socketUri) &&
    hasTargetSocketUri(connection, targetSocketUri)
  );
}

export function hasSocketUri(connection, socketUri) {
  return (
    !!connection && !!socketUri && get(connection, "socketUri") === socketUri
  );
}

export function hasTargetSocketUri(connection, socketUri) {
  return (
    !!connection &&
    !!socketUri &&
    get(connection, "targetSocketUri") === socketUri
  );
}

/**
 * Creates a label of the participants and suggestions/requests/invites of a given set of groupChatConnections
 * @param groupChatConnections
 */
export function generateGroupChatMembersLabel(groupChatConnections) {
  const groupMembersSize = groupChatConnections.filter(conn =>
    isConnected(conn)
  ).size;
  const suggestedSize = groupChatConnections.filter(conn => isSuggested(conn))
    .size;
  const invitedSize = groupChatConnections.filter(conn => isRequestSent(conn))
    .size;
  const requestedSize = groupChatConnections.filter(conn =>
    isRequestReceived(conn)
  ).size;

  const createPartOfLabel = (size, entitySingular, entityPlural) => {
    if (size == 0) {
      return "No " + entityPlural + " yet";
    }
    if (size == 1) {
      return size + " " + entitySingular;
    } else if (size > 1) {
      return size + " " + entityPlural;
    }
  };

  let groupMembersLabel = createPartOfLabel(
    groupMembersSize,
    "Group Member",
    "Group Members"
  );

  if (invitedSize > 0) {
    if (groupMembersLabel.length > 0) {
      groupMembersLabel += ", ";
    }
    groupMembersLabel += createPartOfLabel(invitedSize, "Invited", "Invited");
  }

  if (requestedSize > 0) {
    if (groupMembersLabel.length > 0) {
      groupMembersLabel += ", ";
    }
    groupMembersLabel += createPartOfLabel(
      requestedSize,
      "Request",
      "Requests"
    );
  }

  if (suggestedSize > 0) {
    if (groupMembersLabel.length > 0) {
      groupMembersLabel += ", ";
    }
    groupMembersLabel += createPartOfLabel(
      suggestedSize,
      "Suggested",
      "Suggested"
    );
  }

  return groupMembersLabel;
}

/**
 * Removes the socketEntry of a map if the socketCapacity is below 1 (used so that socketCapacity 1 sockets, are not displayed
 * as tabs, and do not show up in the atom-content like the other sockets
 * @param _
 * @param socketType
 * @returns {boolean}
 */
export const filterSingleConnectedSocketCapacityFilter = (_, socketType) =>
  !vocab.socketCapacity[socketType] || vocab.socketCapacity[socketType] > 1;

/**
 * Removes the socketEntry of a map if the socketCapacity is not excatly 1 (used so that only socketCapacity 1 sockets, are displayed
 * within the WonAtomContentGeneral component
 * @param _
 * @param socketType
 * @returns {boolean}
 */
export const filterNonSingleConnectedSocketCapacityFilter = (_, socketType) =>
  vocab.socketCapacity[socketType] === 1;
<|endoftext|>


<commit_before>Application.Directives.directive('wizardStepDone', wizardStepDoneDirective);

function wizardStepDoneDirective() {
    return {
        scope: {},
        controller: "wizardStepDoneDirectiveController",
        restrict: "EA",
        templateUrl: "application/core/projects/directives/provenance/wizard-step-done.html"
    };
}

Application.Controllers.controller('wizardStepDoneDirectiveController',
                                   ["$scope", "provStep", "actionStatus", "$stateParams", "Restangular", "User", "$timeout",
                                    wizardStepDoneDirectiveController]);
function wizardStepDoneDirectiveController($scope, provStep, actionStatus, $stateParams, Restangular, User, $timeout) {
    var state = actionStatus.getCurrentActionState($stateParams.id);
    $scope.unfinishedSteps = [];
    function determineDoneState() {
        if (!state.currentDraft.process.done) {
            $scope.unfinishedSteps.push({
                stepType: "process",
                step: "process",
                name: state.selectedTemplate.template_name
            });
        }
        state.selectedTemplate.input_templates.forEach(function(t) {
            if (!state.currentDraft.inputs[t.id].done) {
                $scope.unfinishedSteps.push({
                    stepType: "inputs",
                    step: t.id,
                    name: "(I) " + t.template_name
                });
            }
        });

        if (state.selectedTemplate.required_input_files) {
            if (!state.currentDraft.inputs.files.done) {
                $scope.unfinishedSteps.push({
                    stepType: "inputs",
                    step: "files",
                    name: "(I) Files"
                });
            }
        }

        state.selectedTemplate.output_templates.forEach(function(t) {
            if (!state.currentDraft.outputs[t.id].done) {
                $scope.unfinishedSteps.push({
                    stepType: "outputs",
                    step: t.id,
                    name: "(O) " + t.template_name
                });
            }
        });

        if (state.selectedTemplate.required_output_files) {
            if (!state.currentDraft.outputs.files.done) {
                $scope.unfinishedSteps.push({
                    stepType: "outputs",
                    step: "files",
                    name: "(O) Files"
                });
            }
        }

        if ($scope.unfinishedSteps.length === 0) {
            state.currentDraft.completed = true;
        }
    }

    determineDoneState();

    $scope.gotoStep = function(stepType, stepName) {
        var step = provStep.makeStep(stepType, stepName);
        provStep.setStep($stateParams.id, step);
    };

    function closeProvenanceAction() {
        provStep.resetProject($stateParams.id);
        actionStatus.clearCurrentActionState($stateParams.id);
        actionStatus.toggleCurrentAction($stateParams.id);
    }

    $scope.submit = function() {
        //console.log("Submitting %O", state.currentDraft);
        Restangular.one("provenance2")
            .post($stateParams.id,
                  state.currentDraft,
                  {apikey: User.apikey()})
            .then(closeProvenanceAction);
    };

    $scope.saveDraft = function() {
        Restangular.one("drafts2")
            .post($stateParams.id,
                  state.currentDraft,
                  {apikey: User.apikey()})
            .then(function(id) {
                console.dir(id);
                closeProvenanceAction();
            });
    };

    $scope.cancel = function() {
        // The bootstrap modal black backdrop isn't dismissed
        // if we don't wrap these functions in a timeout. It
        // appears that the changes to the dom are happening
        // too quickly.
        $timeout(closeProvenanceAction, 100);
    };
}
<commit_msg>Remove console statement.<commit_after>Application.Directives.directive('wizardStepDone', wizardStepDoneDirective);

function wizardStepDoneDirective() {
    return {
        scope: {},
        controller: "wizardStepDoneDirectiveController",
        restrict: "EA",
        templateUrl: "application/core/projects/directives/provenance/wizard-step-done.html"
    };
}

Application.Controllers.controller('wizardStepDoneDirectiveController',
                                   ["$scope", "provStep", "actionStatus", "$stateParams", "Restangular", "User", "$timeout",
                                    wizardStepDoneDirectiveController]);
function wizardStepDoneDirectiveController($scope, provStep, actionStatus, $stateParams, Restangular, User, $timeout) {
    var state = actionStatus.getCurrentActionState($stateParams.id);
    $scope.unfinishedSteps = [];
    function determineDoneState() {
        if (!state.currentDraft.process.done) {
            $scope.unfinishedSteps.push({
                stepType: "process",
                step: "process",
                name: state.selectedTemplate.template_name
            });
        }
        state.selectedTemplate.input_templates.forEach(function(t) {
            if (!state.currentDraft.inputs[t.id].done) {
                $scope.unfinishedSteps.push({
                    stepType: "inputs",
                    step: t.id,
                    name: "(I) " + t.template_name
                });
            }
        });

        if (state.selectedTemplate.required_input_files) {
            if (!state.currentDraft.inputs.files.done) {
                $scope.unfinishedSteps.push({
                    stepType: "inputs",
                    step: "files",
                    name: "(I) Files"
                });
            }
        }

        state.selectedTemplate.output_templates.forEach(function(t) {
            if (!state.currentDraft.outputs[t.id].done) {
                $scope.unfinishedSteps.push({
                    stepType: "outputs",
                    step: t.id,
                    name: "(O) " + t.template_name
                });
            }
        });

        if (state.selectedTemplate.required_output_files) {
            if (!state.currentDraft.outputs.files.done) {
                $scope.unfinishedSteps.push({
                    stepType: "outputs",
                    step: "files",
                    name: "(O) Files"
                });
            }
        }

        if ($scope.unfinishedSteps.length === 0) {
            state.currentDraft.completed = true;
        }
    }

    determineDoneState();

    $scope.gotoStep = function(stepType, stepName) {
        var step = provStep.makeStep(stepType, stepName);
        provStep.setStep($stateParams.id, step);
    };

    function closeProvenanceAction() {
        provStep.resetProject($stateParams.id);
        actionStatus.clearCurrentActionState($stateParams.id);
        actionStatus.toggleCurrentAction($stateParams.id);
    }

    $scope.submit = function() {
        //console.log("Submitting %O", state.currentDraft);
        Restangular.one("provenance2")
            .post($stateParams.id,
                  state.currentDraft,
                  {apikey: User.apikey()})
            .then(closeProvenanceAction);
    };

    $scope.saveDraft = function() {
        Restangular.one("drafts2")
            .post($stateParams.id,
                  state.currentDraft,
                  {apikey: User.apikey()})
            .then(function(id) {
                closeProvenanceAction();
            });
    };

    $scope.cancel = function() {
        // The bootstrap modal black backdrop isn't dismissed
        // if we don't wrap these functions in a timeout. It
        // appears that the changes to the dom are happening
        // too quickly.
        $timeout(closeProvenanceAction, 100);
    };
}
<|endoftext|>


<commit_before>Application.Controllers.controller("projectSideboard",
    ["$scope", "ui", "sideboard",
        projectSideboard]);
function projectSideboard($scope, ui, sideboard) {
    var result = [];
    if ($scope.empty) {
        result = sideboard.get($scope.project.id);
        $scope.sideboard = result.emptySideBoard;
    } else {
        result = sideboard.get($scope.project.id);
        $scope.sideboard = result.sideboard;
    }

    $scope.toggleExpanded = function() {
        ui.toggleIsExpanded($scope.project.id, "sideboard");
    };

    $scope.isExpanded = function() {
        return ui.isExpanded($scope.project.id, "sideboard");
    };

    $scope.minimize = function() {
        ui.togglePanelState($scope.project.id, 'sideboard');
    };

    $scope.splitScreen = function (what, col) {
        //ui.toggleColumns($scope.project.id, what, col);
        //ui.toggleIsExpanded($scope.project.id, "sideboard");

        ui.togglesideboards($scope.project.id);
        ui.toggleIsExpanded($scope.project.id, "sideboard");

    };

    $scope.isSplitExpanded = function () {
        return ui.getSplitStatus($scope.project.id);
    };

    $scope.onDrop = function (ignore, item, event) {
        if ($scope.empty) {
            sideboard.handleFromEvent($scope.project.id, item, event, 'emptySideBoard');
        } else {
            var i = _.indexOf($scope.sideboard, function (f) {
                return f.id === item.id;
            });
            // If item is already in list then don't add it again.
            if (i === -1) {
                sideboard.handleFromEvent($scope.project.id, item, event, 'sideboard');
            }
        }
    };
}
<commit_msg>Switch to IIFE<commit_after>(function (module) {
    module.controller("projectSideboard", projectSideboard);
    projectSideboard.$inject = ["$scope", "ui", "sideboard"];

    function projectSideboard($scope, ui, sideboard) {
        var result = [];
        if ($scope.empty) {
            result = sideboard.get($scope.project.id);
            $scope.sideboard = result.emptySideBoard;
        } else {
            result = sideboard.get($scope.project.id);
            $scope.sideboard = result.sideboard;
        }

        $scope.toggleExpanded = function () {
            ui.toggleIsExpanded($scope.project.id, "sideboard");
        };

        $scope.isExpanded = function () {
            return ui.isExpanded($scope.project.id, "sideboard");
        };

        $scope.minimize = function () {
            ui.togglePanelState($scope.project.id, 'sideboard');
        };

        $scope.splitScreen = function () {
            ui.togglesideboards($scope.project.id);
            ui.toggleIsExpanded($scope.project.id, "sideboard");

        };

        $scope.isSplitExpanded = function () {
            return ui.getSplitStatus($scope.project.id);
        };

        $scope.onDrop = function (ignore, item, event) {
            if ($scope.empty) {
                sideboard.handleFromEvent($scope.project.id, item, event, 'emptySideBoard');
            } else {
                var i = _.indexOf($scope.sideboard, function (f) {
                    return f.id === item.id;
                });
                // If item is already in list then don't add it again.
                if (i === -1) {
                    sideboard.handleFromEvent($scope.project.id, item, event, 'sideboard');
                }
            }
        };
    }
}(angular.module('materialscommons')));
<|endoftext|>


<commit_before>import { isFunction } from './types.js';

let support = isFunction(Symbol);
let count = 0;

/**
 * Create a getter/setter for symbolic objects's properties.
 *
 * @class Symbolic
 */
export default class Symbolic {
    constructor(property) {
        if (support) {
            this.SYM = Symbol(property);
        } else {
            this.SYM = `__${property}_${count++}`;
        }
    }

    /**
     * Check if an object has a Symbolic definition.
     *
     * @param {Object} object The scope to check if it has the symbol
     * @return {boolean} The scope has the symbol or not
     */
    has(object) {
        return object.hasOwnProperty(this);
    }

    toString() {
        return this.SYM;
    }
}

try {
    if (support) {
        let check = new Symbolic('check');
        let test = {};
        test[check] = 2;
    }
} catch(ex) {
    support = false;
}
<commit_msg>fix: edge support<commit_after>let support = typeof Symbol !== 'function';
let count = 0;

/**
 * Create a getter/setter for symbolic objects's properties.
 *
 * @class Symbolic
 */
export default class Symbolic {
    constructor(property) {
        if (support) {
            this.SYM = Symbol(property);
        } else {
            this.SYM = `__${property}_${count++}`;
        }
    }

    /**
     * Check if an object has a Symbolic definition.
     *
     * @param {Object} object The scope to check if it has the symbol
     * @return {boolean} The scope has the symbol or not
     */
    has(object) {
        return object.hasOwnProperty(this);
    }

    toString() {
        return this.SYM;
    }
}

try {
    if (support) {
        let check = new Symbolic('check');
        let test = {};
        test[check] = 2;
    }
} catch(ex) {
    support = false;
}
<|endoftext|>


<commit_before>const Evaluator = require('./evaluator');
const FilterManager = require('./filter-manager');
const Hash = require('./data.hash');
const QueueHash = require('./data.queueHash');
const parser = require('./parser');

class AstCache {
  constructor() {
    this._parser = parser.init();
    this._cache = new Hash();
  }

  /**
   * @param {string} source
   * @param {string} content
   */
  save(source, content) {
    if (!this._cache.has(source)) {
      this._cache.add(source, this._parser.parse(content, source));
    }
  }

  /**
   * @param {string} source
   * @return {boolean}
   */
  has(source) {
    return this._cache.has(source);
  }

  /**
   * @param {string} source
   * @return {Object}
   */
  get(source) {
    return this._cache.get(source);
  }

  clear() {
    this._cache.clear();
  }
}

class RemoteQueue {
  /**
   * @param {Evaluator} evaluator
   * @param {AstCache} astCache
   */
  constructor(evaluator, astCache) {
    this._evaluator = evaluator;
    this._astCache = astCache;
    this._queueHash = new QueueHash();
  }

  /**
   * @param {string} source
   * @param {{param: Object, callback: function}} data
   */
  push(source, data) {
    this._queueHash.addTo(source, {
      param: data.param,
      callback: data.callback,
    });
  }

  /**
   * @param {string} source
   * @throws {Error}
   */
  consume(source) {
    let queue;

    while (this._queueHash.sizeOf(source) > 0) {
      queue = this._queueHash.getFrom(source);
      queue.callback(this._evaluate(source, queue.param));
    }
  }

  /**
   * @param {string} source
   * @param {Object[]} param
   * @throws {Error}
   * @private
   */
  _evaluate(source, param) {
    try {
      return this._evaluator.evaluate(this._astCache.get(source), [param]);
    } catch (e) {
      throw new Error('template - ' + e.message + ' in source ' + source);
    }
  }
}

class PrefetchManager {
  /**
   * @param {AstCache} astCache
   */
  constructor(astCache) {
    this._jobList = [];
    this._astCache = astCache;
  }

  /**
   * @param {string[]} sourceList
   * @param {function} callback
   */
  add(sourceList, callback) {
    this._jobList.push({
      sourceList: sourceList,
      callback: callback,
    });
  }

  notifyFetched() {
    const undone = [];
    const finished = [];

    this._jobList.forEach((job) => {
      const unacquired = job.sourceList.filter((source) => {
        return !this._astCache.has(source);
      });

      if (unacquired.length > 0) {
        undone.push(job);
      } else {
        finished.push(job);
      }
    });

    this._jobList = undone;

    finished.forEach((job) => {
      if (typeof job.callback === 'function') {
        job.callback();
      }
    });
  }
}

const template = () => {
  const that = {};

  const _filterManager = new FilterManager();

  const _evaluator = new Evaluator(_filterManager);

  const _astCache = new AstCache();

  const _prefetchManager = new PrefetchManager(_astCache);

  const _remoteQueue = new RemoteQueue(_evaluator, _astCache);

  /**
   * @param {string} source
   * @param {string} content
   */
  const _register = (source, content) => {
    _astCache.save(source, content);
  };

  const _registerFromRemote = (() => {
    const _fetching = new Hash();

    const _fetchRemoteSource = (source, data, callback) => {
      const req = new XMLHttpRequest();

      req.open('GET', source, true);
      req.onreadystatechange = () => {
        if (req.readyState !== 4 || req.status !== 200) {
          return;
        }

        callback(req.responseText);
      };
      req.send();
    };

    return (source, data) => {
      if (data.render) {
        _remoteQueue.push(source, data.render);
      }

      if (_astCache.has(source)) {
        _remoteQueue.consume(source);
      } else {
        if (_fetching.has(source)) {
          return;
        }

        _fetching.add(source, true);
        _fetchRemoteSource(source, data, (content) => {
          if (data.check && typeof data.check.callback === 'function') {
            data.check.callback(source);
          }

          _astCache.save(source, content);
          _fetching.remove(source);
          _prefetchManager.notifyFetched();
          _remoteQueue.consume(source);
        });
      }
    };
  })();

  /**
   * @param {string} source
   * @param {Object} bindParams
   * @return {string}
   */
  const _evaluate = (source, bindParams) => {
    try {
      return _evaluator.evaluate(_astCache.get(source), [bindParams]);
    } catch (e) {
      throw new Error('template - ' + e.message + ' in source ' + source);
    }
  };

  /**
   * @param {string} name
   * @param {function} func
   */
  that.addFilter = (name, func) => {
    _filterManager.register(name, func);
  };

  /**
   * @param {string} source
   * @param {Object} param
   */
  that.render = (source, param) => {
    _register(source, source);

    return _evaluate(source, param);
  };

  /**
   * @param {string} source
   * @param {Object} param
   * @param {function} callback
   */
  that.renderAsync = (source, param, callback) => {
    _registerFromRemote(source, {
      render: {
        param: param,
        callback: callback,
      },
    });
  };

  /**
   * @param {string|string[]} target
   * @param {function} callback Called once when all sources fetched.
   * @param {function} [checkCallback] Called every time when a source fetched.
   */
  that.prefetch = (target, callback, checkCallback) => {
    const sourceList = (typeof target === 'string') ? [].concat(target) : target;

    sourceList.forEach((source) => {
      _registerFromRemote(source, {
        check: {
          callback: checkCallback,
        },
      });
    });

    _prefetchManager.add(sourceList, callback);
  };

  that.clearTemplateCache = () => {
    _astCache.clear();
  };

  registerPredefinedFilters(_filterManager);

  return that;
};

/**
 * @param {FilterManager} filterManager
 */
const registerPredefinedFilters = (filterManager) => {
  filterManager.register('h', (() => {
    const list = {
      '<': '<',
      '>': '>',
      '&': '&',
      '"': '"',
      "'": ''', // eslint-disable-line quotes
    };

    return (value) => {
      return value.replace(/[<>&"']/g, (matched) => {
        return list[matched];
      });
    };
  })());

  filterManager.register('default', (value, defaultValue) => {
    return value.length === 0 ? defaultValue : value;
  });

  filterManager.register('upper', (value) => {
    return value.toLocaleUpperCase();
  });

  filterManager.register('lower', (value) => {
    return value.toLocaleLowerCase();
  });

  filterManager.register('plus', (value, plus) => {
    if (isFinite(value) && typeof plus === 'number' && isFinite(plus)) {
      return (+(value) + plus).toString();
    } else {
      return value;
    }
  });
};

module.exports.init = () => {
  return template();
};
<commit_msg>AstCache.evaluate()<commit_after>const Evaluator = require('./evaluator');
const FilterManager = require('./filter-manager');
const Hash = require('./data.hash');
const QueueHash = require('./data.queueHash');
const parser = require('./parser');

class AstCache {
  /**
   * @param {Evaluator} evaluator
   */
  constructor(evaluator) {
    this._parser = parser.init();
    this._evaluator = evaluator;
    this._cache = new Hash();
  }

  /**
   * @param {string} source
   * @param {string} content
   */
  save(source, content) {
    if (!this._cache.has(source)) {
      this._cache.add(source, this._parser.parse(content, source));
    }
  }

  /**
   * @param {string} source
   * @return {boolean}
   */
  has(source) {
    return this._cache.has(source);
  }

  /**
   * @param {string} source
   * @return {Object}
   */
  get(source) {
    return this._cache.get(source);
  }

  /**
   * @param {string} source
   * @param {Object} param
   * @return {string}
   * @throws {Error}
   */
  evaluate(source, param) {
    try {
      return this._evaluator.evaluate(this._cache.get(source), [param]);
    } catch (e) {
      throw new Error('template - ' + e.message + ' in source ' + source);
    }
  }

  clear() {
    this._cache.clear();
  }
}

class RemoteQueue {
  /**
   * @param {AstCache} astCache
   */
  constructor(astCache) {
    this._astCache = astCache;
    this._queueHash = new QueueHash();
  }

  /**
   * @param {string} source
   * @param {{param: Object, callback: function}} data
   */
  push(source, data) {
    this._queueHash.addTo(source, {
      param: data.param,
      callback: data.callback,
    });
  }

  /**
   * @param {string} source
   * @throws {Error}
   */
  consume(source) {
    let queue;

    while (this._queueHash.sizeOf(source) > 0) {
      queue = this._queueHash.getFrom(source);
      queue.callback(this._astCache.evaluate(source, queue.param));
    }
  }
}

class PrefetchManager {
  /**
   * @param {AstCache} astCache
   */
  constructor(astCache) {
    this._jobList = [];
    this._astCache = astCache;
  }

  /**
   * @param {string[]} sourceList
   * @param {function} callback
   */
  add(sourceList, callback) {
    this._jobList.push({
      sourceList: sourceList,
      callback: callback,
    });
  }

  notifyFetched() {
    const undone = [];
    const finished = [];

    this._jobList.forEach((job) => {
      const unacquired = job.sourceList.filter((source) => {
        return !this._astCache.has(source);
      });

      if (unacquired.length > 0) {
        undone.push(job);
      } else {
        finished.push(job);
      }
    });

    this._jobList = undone;

    finished.forEach((job) => {
      if (typeof job.callback === 'function') {
        job.callback();
      }
    });
  }
}

const template = () => {
  const that = {};

  const _filterManager = new FilterManager();

  const _astCache = new AstCache(new Evaluator(_filterManager));

  const _prefetchManager = new PrefetchManager(_astCache);

  const _remoteQueue = new RemoteQueue(_astCache);

  /**
   * @param {string} source
   * @param {string} content
   */
  const _register = (source, content) => {
    _astCache.save(source, content);
  };

  const _registerFromRemote = (() => {
    const _fetching = new Hash();

    const _fetchRemoteSource = (source, data, callback) => {
      const req = new XMLHttpRequest();

      req.open('GET', source, true);
      req.onreadystatechange = () => {
        if (req.readyState !== 4 || req.status !== 200) {
          return;
        }

        callback(req.responseText);
      };
      req.send();
    };

    return (source, data) => {
      if (data.render) {
        _remoteQueue.push(source, data.render);
      }

      if (_astCache.has(source)) {
        _remoteQueue.consume(source);
      } else {
        if (_fetching.has(source)) {
          return;
        }

        _fetching.add(source, true);
        _fetchRemoteSource(source, data, (content) => {
          if (data.check && typeof data.check.callback === 'function') {
            data.check.callback(source);
          }

          _astCache.save(source, content);
          _fetching.remove(source);
          _prefetchManager.notifyFetched();
          _remoteQueue.consume(source);
        });
      }
    };
  })();

  /**
   * @param {string} name
   * @param {function} func
   */
  that.addFilter = (name, func) => {
    _filterManager.register(name, func);
  };

  /**
   * @param {string} source
   * @param {Object} param
   * @return {string}
   * @throws {Error}
   */
  that.render = (source, param) => {
    _register(source, source);

    return _astCache.evaluate(source, param);
  };

  /**
   * @param {string} source
   * @param {Object} param
   * @param {function} callback
   */
  that.renderAsync = (source, param, callback) => {
    _registerFromRemote(source, {
      render: {
        param: param,
        callback: callback,
      },
    });
  };

  /**
   * @param {string|string[]} target
   * @param {function} callback Called once when all sources fetched.
   * @param {function} [checkCallback] Called every time when a source fetched.
   */
  that.prefetch = (target, callback, checkCallback) => {
    const sourceList = (typeof target === 'string') ? [].concat(target) : target;

    sourceList.forEach((source) => {
      _registerFromRemote(source, {
        check: {
          callback: checkCallback,
        },
      });
    });

    _prefetchManager.add(sourceList, callback);
  };

  that.clearTemplateCache = () => {
    _astCache.clear();
  };

  registerPredefinedFilters(_filterManager);

  return that;
};

/**
 * @param {FilterManager} filterManager
 */
const registerPredefinedFilters = (filterManager) => {
  filterManager.register('h', (() => {
    const list = {
      '<': '<',
      '>': '>',
      '&': '&',
      '"': '"',
      "'": ''', // eslint-disable-line quotes
    };

    return (value) => {
      return value.replace(/[<>&"']/g, (matched) => {
        return list[matched];
      });
    };
  })());

  filterManager.register('default', (value, defaultValue) => {
    return value.length === 0 ? defaultValue : value;
  });

  filterManager.register('upper', (value) => {
    return value.toLocaleUpperCase();
  });

  filterManager.register('lower', (value) => {
    return value.toLocaleLowerCase();
  });

  filterManager.register('plus', (value, plus) => {
    if (isFinite(value) && typeof plus === 'number' && isFinite(plus)) {
      return (+(value) + plus).toString();
    } else {
      return value;
    }
  });
};

module.exports.init = () => {
  return template();
};
<|endoftext|>


<commit_before>define('mygame', ['lyria/game', 'mygame/scenelist', 'mygame/assetlist'], function(Game, sceneList, assetList) {
  'use strict';

  // Create a new game object
	var myGame = new Game();

	// Set generated scene files
	myGame.director.precompiledScenes = sceneList;

	// If preloader is complete, everything in this function happens
	myGame.preloader.on('complete', function() {
	  // Add "scene1" to director
    myGame.director.add('scene1');
    // Add "scene2" to director
    myGame.director.add('scene2');

    // Show "scene1"
    myGame.director.show('scene1');
  });

  // Set asset list for preloader
  myGame.preloader.assets = assetList;

  // Spin up the preloader
  myGame.preloader.start();

});<commit_msg>API change: Director.precompiledScenes is now Director.scenes<commit_after>define('mygame', ['lyria/game', 'mygame/scenelist', 'mygame/assetlist'], function(Game, sceneList, assetList) {
  'use strict';

  // Create a new game object
	var myGame = new Game();

	// Set generated scene files
	myGame.director.scenes = sceneList;

	// If preloader is complete, everything in this function happens
	myGame.preloader.on('complete', function() {
	  // Add "scene1" to director
    myGame.director.add('scene1');
    // Add "scene2" to director
    myGame.director.add('scene2');

    // Show "scene1"
    myGame.director.show('scene1');
  });

  // Set asset list for preloader
  myGame.preloader.assets = assetList;

  // Spin up the preloader
  myGame.preloader.start();

});<|endoftext|>


<commit_before>Box.Application.addModule('radiator-controller', function(context) {
  'use strict';

  var utils, store, windowSvc, intercom, d3, $, moduleElem, cubismGraphs;

  var render = {
    horizon: function() {
      var host          = windowSvc.getHashVariable("host"),
          metric        = windowSvc.getHashVariable("metric"),
          chartConfig   = {
            metric:      metric,
            host:        host,
            instanceTag: "host_" + host
          };

      cubismGraphs.resetCubismContext();
      $(window).resize(utils.debouncer(cubismGraphs.resetCubismContext));
      moduleElem.call(createHorizonGraph, chartConfig);
      context.broadcast("reload-graph-configuration");
      windowSvc.setTitle(metric + " for " + host);
    }
  };

  function initGraph(type) {
    if (!render[type]) {
      throw new Error('Graph type not found.');
    }

    render[type]();
  }

  function radiatorGraphIdPrefix(radiatorId) {
    return "live:radiator:" + radiatorId + ":graph-";
  }

  function injectModuleConfiguration(horizonGraphElement, radiatorIdPrefix, chartConfig) {
    var radiatorConfig = {
      configurationIdPrefix: radiatorIdPrefix,
      disableTerminalButton: true,
      disableRadiatorShareButton: false,
      chart: chartConfig
    };
    utils.setConfigurationElement(horizonGraphElement, radiatorConfig);
  }

  function createHorizonGraph(parentElement, chartConfig) {
    var radiatorIdPrefix = radiatorGraphIdPrefix('name');
    var horizonGraphElement = parentElement.append("div")
      .attr("data-module","horizon-graph");
    injectModuleConfiguration(horizonGraphElement, radiatorIdPrefix, chartConfig);
    Box.Application.start(horizonGraphElement[0][0]);
  }

  return {
    init: function() {
      intercom   = context.getGlobal("Intercom").getInstance();
      d3         = context.getGlobal("d3");
      $          = context.getGlobal("jQuery");
      moduleElem = d3.select(context.getElement());

      utils          = context.getService("utils");
      windowSvc      = context.getService("window");
      store          = context.getService("configuration-store");
      cubismGraphs   = context.getService("cubism-graphs");

      initGraph( windowSvc.getHashVariable("type") );
    },

    messages: ["timescale-changed"],

    onmessage: function(name, timescale) {
      switch (name) {
        case 'timescale-changed':
          cubismGraphs.resetCubismContext();
          break;
      }
    }
  };
});
<commit_msg>uppercase metric in title<commit_after>Box.Application.addModule('radiator-controller', function(context) {
  'use strict';

  var utils, store, windowSvc, intercom, d3, $, moduleElem, cubismGraphs;

  var render = {
    horizon: function() {
      var host          = windowSvc.getHashVariable("host"),
          metric        = windowSvc.getHashVariable("metric"),
          chartConfig   = {
            metric:      metric,
            host:        host,
            instanceTag: "host_" + host
          };

      cubismGraphs.resetCubismContext();
      $(window).resize(utils.debouncer(cubismGraphs.resetCubismContext));
      moduleElem.call(createHorizonGraph, chartConfig);
      context.broadcast("reload-graph-configuration");
      windowSvc.setTitle(metric.toUpperCase() + " for " + host);
    }
  };

  function initGraph(type) {
    if (!render[type]) {
      throw new Error('Graph type not found.');
    }

    render[type]();
  }

  function radiatorGraphIdPrefix(radiatorId) {
    return "live:radiator:" + radiatorId + ":graph-";
  }

  function injectModuleConfiguration(horizonGraphElement, radiatorIdPrefix, chartConfig) {
    var radiatorConfig = {
      configurationIdPrefix: radiatorIdPrefix,
      disableTerminalButton: true,
      disableRadiatorShareButton: false,
      chart: chartConfig
    };
    utils.setConfigurationElement(horizonGraphElement, radiatorConfig);
  }

  function createHorizonGraph(parentElement, chartConfig) {
    var radiatorIdPrefix = radiatorGraphIdPrefix('name');
    var horizonGraphElement = parentElement.append("div")
      .attr("data-module","horizon-graph");
    injectModuleConfiguration(horizonGraphElement, radiatorIdPrefix, chartConfig);
    Box.Application.start(horizonGraphElement[0][0]);
  }

  return {
    init: function() {
      intercom   = context.getGlobal("Intercom").getInstance();
      d3         = context.getGlobal("d3");
      $          = context.getGlobal("jQuery");
      moduleElem = d3.select(context.getElement());

      utils          = context.getService("utils");
      windowSvc      = context.getService("window");
      store          = context.getService("configuration-store");
      cubismGraphs   = context.getService("cubism-graphs");

      initGraph( windowSvc.getHashVariable("type") );
    },

    messages: ["timescale-changed"],

    onmessage: function(name, timescale) {
      switch (name) {
        case 'timescale-changed':
          cubismGraphs.resetCubismContext();
          break;
      }
    }
  };
});
<|endoftext|>


<commit_before>const slideTimeoutTime = 40;
let slideTimeout;

Template.imageControls.onRendered(() => {
    const instance = Template.instance();

    Meteor.defer(() => {
        // Set the current imageSlider width to its parent's height
        // (because webkit is stupid and can't style vertical sliders)
        const $slider = instance.$('#imageSlider');
        const $element = $slider.parents().eq(2).siblings('.imageViewerViewport');
        const viewportHeight = $element.height();

        $slider.width(viewportHeight - 20);
    });
})

Template.imageControls.events({
    'keydown #imageSlider'(event) {
        // We don't allow direct keyboard up/down input on the
        // image sliders since the natural direction is reversed (0 is at the top)

        // Prevent the browser's default behaviour (scrolling)
        event.preventDefault();

        // Store the KeyCodes in an object for readability
        const keys = {
            DOWN: 40,
            UP: 38
        };

        if (event.which === keys.DOWN) {
            OHIF.viewer.hotkeyFunctions.scrollDown();
        } else if (event.which === keys.UP) {
            OHIF.viewer.hotkeyFunctions.scrollUp();
        }
    },
    'input #imageSlider, change #imageSlider'(event) {
        // Note that we throttle requests to prevent the
        // user's ultrafast scrolling from firing requests too quickly.
        clearTimeout(slideTimeout);
        slideTimeout = setTimeout(() => {
            // Using the slider in an inactive viewport
            // should cause that viewport to become active
            const slider = $(event.currentTarget);
            const newActiveElement = slider.parents().eq(2).siblings('.imageViewerViewport').get(0);
            setActiveViewport(newActiveElement);

            // Subtract 1 here since the slider goes from 1 to N images
            // But the stack indexing starts at 0
            const newImageIdIndex = parseInt(slider.val(), 10) - 1;
            switchToImageByIndex(newImageIdIndex);
        }, slideTimeoutTime);

        return false;
    }
});
<commit_msg>Fix bug in Image Control slider<commit_after>const slideTimeoutTime = 40;
let slideTimeout;

Template.imageControls.onRendered(() => {
    const instance = Template.instance();

    // Set the current imageSlider width to its parent's height
    // (because webkit is stupid and can't style vertical sliders)
    const $slider = instance.$('#imageSlider');
    const $element = $slider.parents().eq(2).siblings('.imageViewerViewport');
    const viewportHeight = $element.height();

    $slider.width(viewportHeight - 20);
})

Template.imageControls.events({
    'keydown #imageSlider'(event) {
        // We don't allow direct keyboard up/down input on the
        // image sliders since the natural direction is reversed (0 is at the top)

        // Prevent the browser's default behaviour (scrolling)
        event.preventDefault();

        // Store the KeyCodes in an object for readability
        const keys = {
            DOWN: 40,
            UP: 38
        };

        if (event.which === keys.DOWN) {
            OHIF.viewer.hotkeyFunctions.scrollDown();
        } else if (event.which === keys.UP) {
            OHIF.viewer.hotkeyFunctions.scrollUp();
        }
    },
    'input #imageSlider, change #imageSlider'(event) {
        // Note that we throttle requests to prevent the
        // user's ultrafast scrolling from firing requests too quickly.
        clearTimeout(slideTimeout);
        slideTimeout = setTimeout(() => {
            // Using the slider in an inactive viewport
            // should cause that viewport to become active
            const slider = $(event.currentTarget);
            const newActiveElement = slider.parents().eq(2).siblings('.imageViewerViewport').get(0);
            setActiveViewport(newActiveElement);

            // Subtract 1 here since the slider goes from 1 to N images
            // But the stack indexing starts at 0
            const newImageIdIndex = parseInt(slider.val(), 10) - 1;
            switchToImageByIndex(newImageIdIndex);
        }, slideTimeoutTime);

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


<commit_before>define( ['dojo/_base/declare',
         'dojo/_base/array',
         'JBrowse/View/Track/Wiggle',
         'JBrowse/Util'
        ],
        function( declare, array, Wiggle, Util ) {

// feature class for the features we make for the calculated coverage
// values
var CoverageFeature = Util.fastDeclare(
    {
        get: function(f) { return this[f]; },
        tags: function() { return [ 'start', 'end', 'score' ]; },
        score: 0,
        constructor: function( args ) {
            this.start = args.start;
            this.end = args.end;
            this.score = args.score;
        }
    });

return declare( Wiggle,
{

    _defaultConfig: function() {
        return Util.deepUpdate(
            dojo.clone( this.inherited(arguments) ),
            {
                min_score: 0,
                max_score: 100
            }
        );
    },

    getGlobalStats: function() {
        return {};
    },

    getFeatures: function( query, featureCallback, finishCallback, errorCallback ) {
        var leftBase  = query.start;
        var rightBase = query.end;
        var scale = query.scale; // px/bp
        var widthBp = rightBase-leftBase;
        var widthPx = widthBp * ( query.scale || 1/query.basesPerSpan);

        var binWidth = Math.ceil( query.basesPerSpan ); // in bp

        var arraySize = Math.ceil( widthBp/binWidth );
        var coverageBins = new Array( arraySize );
        var aCoverage = new Array( arraySize );
        var tCoverage = new Array( arraySize );
        var cCoverage = new Array( arraySize );
        var gCoverage = new Array( arraySize );
        var bpToBin = function( bp ) {
            return Math.floor( (bp-leftBase) / binWidth );
        };

        this.store.getFeatures(
            query,
            dojo.hitch( this, function( feature ) {
                            var startBin = bpToBin( feature.get('start') );
                            var endBin   = bpToBin( feature.get('end')-1 );
                            for( var i = startBin; i <= endBin; i++ ) {
                                coverageBins[i] = (coverageBins[i] || 0) + 1;
                            }
                            // Calculate SNP coverage
                            var mdTag = feature.get('MD');
                            if(mdTag) {
                                var SNPs = this._mdToMismatches(feature, mdTag);
                                // loops through mismatches and updates coverage variables accordingly.
                                for (var i = 0; i<SNPs.length; i++) {
                                    var pos = feature.get('start') + SNPs[i].start - leftBase;
                                    switch (SNPs[i].bases) {
                                        case "A": aCoverage[pos] = (aCoverage[pos] || 0) + 1; break;
                                        case "T": tCoverage[pos] = (tCoverage[pos] || 0) + 1; break;
                                        case "C": cCoverage[pos] = (cCoverage[pos] || 0) + 1; break;
                                        case "G": gCoverage[pos] = (gCoverage[pos] || 0) + 1; break;
                                        default: alert("Unknown base encountered: " + SNPs[i].bases);
                                    }
                                }
                            }
                        }),
            function () {
                // make fake features from the coverage
                for( var i = 0; i < coverageBins.length; i++ ) {
                    // score contains [non-SNP coverage, a SNPs, t SNPs, c SNPs, g SNPs]
                    var nonSNP = (coverageBins[i] || 0) - (aCoverage[i] || 0) - (tCoverage[i] || 0) - (cCoverage[i] || 0) - (gCoverage[i] || 0);
                    var score = [nonSNP, aCoverage[i] || 0, tCoverage[i] || 0, cCoverage[i] || 0, gCoverage[i] || 0];
                    var bpOffset = leftBase+binWidth*i;
                    featureCallback( new CoverageFeature({
                        start: bpOffset,
                        end:   bpOffset+binWidth,
                        score: score
                     }));
                }
                finishCallback();
            }
        );
    },

    /***********************************************************************************************************************
     * Draw a set of features on the canvas.
     * @private
     */
    _drawFeatures: function( scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale ) {
        var context = canvas.getContext('2d');
        var canvasHeight = canvas.height;
        var toY = dojo.hitch( this, function( val ) {
           return canvasHeight * ( 1-dataScale.normalize.call(this, val) );
        });
        var originY = toY( dataScale.origin );

        var barColor  = ['#777', 'green', 'red', 'blue', 'yellow'];
        var clipColor = this.config.style.clip_marker_color;
        var bgColor   = this.config.style.bg_color;
        var disableClipMarkers = this.config.disable_clip_markers;

        dojo.forEach( features, function(f,i) {

            var fRect = featureRects[i];

            var score = f.get('score');

            for (var j = 0; j<5; j++) {

                fRect.t = toY(score.slice(j,5).reduce(function(a,b){return a+b;})); // makes progressively shorter rectangles.

                // draw the background color if we are configured to do so
                if( bgColor && fRect.t >= 0 ) {
                    context.fillStyle = bgColor;
                    context.fillRect( fRect.l, 0, fRect.w, canvasHeight );
                }

                if( fRect.t <= canvasHeight ) { // if the rectangle is visible at all

                    if( fRect.t <= originY ) {
                        // bar goes upward
                        context.fillStyle = barColor[j];
                        context.fillRect( fRect.l, fRect.t, fRect.w, originY-fRect.t+1);
                        if( !disableClipMarkers && fRect.t < 0 ) { // draw clip marker if necessary
                            context.fillStyle = clipColor || negColor;
                            context.fillRect( fRect.l, 0, fRect.w, 2 );
                        }
                    }
                    else {
                        alert('Invalid data used. Negative values are not possible.');
                    }
                }
            }
        }, this );
    },
    //*******************************************************************************************************************************

    // a method blatently stolen from "Alignments.js" Perhaps it would be better just to include the file...
    _mdToMismatches: function( feature, mdstring ) {
        var mismatchRecords = [];
        var curr = { start: 0, bases: '' };
        var seq = feature.get('seq');
        var nextRecord = function() {
              mismatchRecords.push( curr );
              curr = { start: curr.start + curr.bases.length, bases: ''};
        };
        array.forEach( mdstring.match(/(\d+|\^[a-z]+|[a-z])/ig), function( token ) {
          if( token.match(/^\d/) ) { // matching bases
              curr.start += parseInt( token );
          }
          else if( token.match(/^\^/) ) { // insertion in the template
              var i = token.length-1;
              while( i-- ) {
                  curr.bases += '*';
              }
              nextRecord();
          }
          else if( token.match(/^[a-z]/i) ) { // mismatch
              curr.bases = seq.substr( curr.start, token.length );
              nextRecord();
          }
        });
        return mismatchRecords;
    }
});
});<commit_msg>Ignores unknown bases (temporary?)<commit_after>define( ['dojo/_base/declare',
         'dojo/_base/array',
         'JBrowse/View/Track/Wiggle',
         'JBrowse/Util'
        ],
        function( declare, array, Wiggle, Util ) {

// feature class for the features we make for the calculated coverage
// values
var CoverageFeature = Util.fastDeclare(
    {
        get: function(f) { return this[f]; },
        tags: function() { return [ 'start', 'end', 'score' ]; },
        score: 0,
        constructor: function( args ) {
            this.start = args.start;
            this.end = args.end;
            this.score = args.score;
        }
    });

return declare( Wiggle,
{

    _defaultConfig: function() {
        return Util.deepUpdate(
            dojo.clone( this.inherited(arguments) ),
            {
                min_score: 0,
                max_score: 100
            }
        );
    },

    getGlobalStats: function() {
        return {};
    },

    getFeatures: function( query, featureCallback, finishCallback, errorCallback ) {
        var leftBase  = query.start;
        var rightBase = query.end;
        var scale = query.scale; // px/bp
        var widthBp = rightBase-leftBase;
        var widthPx = widthBp * ( query.scale || 1/query.basesPerSpan);

        var binWidth = Math.ceil( query.basesPerSpan ); // in bp

        var arraySize = Math.ceil( widthBp/binWidth );
        var coverageBins = new Array( arraySize );
        var aCoverage = new Array( arraySize );
        var tCoverage = new Array( arraySize );
        var cCoverage = new Array( arraySize );
        var gCoverage = new Array( arraySize );
        var bpToBin = function( bp ) {
            return Math.floor( (bp-leftBase) / binWidth );
        };

        this.store.getFeatures(
            query,
            dojo.hitch( this, function( feature ) {
                            var startBin = bpToBin( feature.get('start') );
                            var endBin   = bpToBin( feature.get('end')-1 );
                            for( var i = startBin; i <= endBin; i++ ) {
                                coverageBins[i] = (coverageBins[i] || 0) + 1;
                            }
                            // Calculate SNP coverage
                            var mdTag = feature.get('MD');
                            if(mdTag) {
                                var SNPs = this._mdToMismatches(feature, mdTag);
                                // loops through mismatches and updates coverage variables accordingly.
                                for (var i = 0; i<SNPs.length; i++) {
                                    var pos = feature.get('start') + SNPs[i].start - leftBase;
                                    switch (SNPs[i].bases) {
                                        case "A": aCoverage[pos] = (aCoverage[pos] || 0) + 1; break;
                                        case "T": tCoverage[pos] = (tCoverage[pos] || 0) + 1; break;
                                        case "C": cCoverage[pos] = (cCoverage[pos] || 0) + 1; break;
                                        case "G": gCoverage[pos] = (gCoverage[pos] || 0) + 1; break;
                                        case "*": break; // unknown base. current version ignores these.
                                        default: alert("Unknown base encountered: " + SNPs[i].bases);
                                    }
                                }
                            }
                        }),
            function () {
                // make fake features from the coverage
                for( var i = 0; i < coverageBins.length; i++ ) {
                    // score contains [non-SNP coverage, a SNPs, t SNPs, c SNPs, g SNPs]
                    var score = [0, aCoverage[i] || 0, tCoverage[i] || 0, cCoverage[i] || 0, gCoverage[i] || 0];
                    score[0] = coverageBins[i] - score.reduce(function(a,b){return a+b;});
                    var bpOffset = leftBase+binWidth*i;
                    featureCallback( new CoverageFeature({
                        start: bpOffset,
                        end:   bpOffset+binWidth,
                        score: score
                     }));
                }
                finishCallback();
            }
        );
    },

    /***********************************************************************************************************************
     * Draw a set of features on the canvas.
     * @private
     */
    _drawFeatures: function( scale, leftBase, rightBase, block, canvas, features, featureRects, dataScale ) {
        var context = canvas.getContext('2d');
        var canvasHeight = canvas.height;
        var toY = dojo.hitch( this, function( val ) {
           return canvasHeight * ( 1-dataScale.normalize.call(this, val) );
        });
        var originY = toY( dataScale.origin );

        var barColor  = ['#999', 'green', 'red', 'blue', 'yellow'];
        var clipColor = this.config.style.clip_marker_color;
        var bgColor   = this.config.style.bg_color;
        var disableClipMarkers = this.config.disable_clip_markers;

        dojo.forEach( features, function(f,i) {

            var fRect = featureRects[i];

            var score = f.get('score');

            for (var j = 0; j<5; j++) {

                fRect.t = toY(score.slice(j,5).reduce(function(a,b){return a+b;})); // makes progressively shorter rectangles.

                // draw the background color if we are configured to do so
                if( bgColor && fRect.t >= 0 ) {
                    context.fillStyle = bgColor;
                    context.fillRect( fRect.l, 0, fRect.w, canvasHeight );
                }

                if( fRect.t <= canvasHeight ) { // if the rectangle is visible at all

                    if( fRect.t <= originY ) {
                        // bar goes upward
                        context.fillStyle = barColor[j];
                        context.fillRect( fRect.l, fRect.t, fRect.w, originY-fRect.t+1);
                        if( !disableClipMarkers && fRect.t < 0 ) { // draw clip marker if necessary
                            context.fillStyle = clipColor || negColor;
                            context.fillRect( fRect.l, 0, fRect.w, 2 );
                        }
                    }
                    else {
                        alert('Invalid data used. Negative values are not possible.');
                    }
                }
            }
        }, this );
    },
    //*******************************************************************************************************************************

    // a method blatently stolen from "Alignments.js" Perhaps it would be better just to include the file...
    _mdToMismatches: function( feature, mdstring ) {
        var mismatchRecords = [];
        var curr = { start: 0, bases: '' };
        var seq = feature.get('seq');
        var nextRecord = function() {
              mismatchRecords.push( curr );
              curr = { start: curr.start + curr.bases.length, bases: ''};
        };
        array.forEach( mdstring.match(/(\d+|\^[a-z]+|[a-z])/ig), function( token ) {
          if( token.match(/^\d/) ) { // matching bases
              curr.start += parseInt( token );
          }
          else if( token.match(/^\^/) ) { // insertion in the template
              var i = token.length-1;
              while( i-- ) {
                  curr.bases += '*';
              }
              nextRecord();
          }
          else if( token.match(/^[a-z]/i) ) { // mismatch
              curr.bases = seq.substr( curr.start, token.length );
              nextRecord();
          }
        });
        return mismatchRecords;
    }
});
});<|endoftext|>


<commit_before>ManageIQ.angular.app.controller('pagingDivButtonGroupController', ['$scope', 'miqService', '$compile', '$attrs', function($scope, miqService, $compile, $attrs) {
  var init = function() {
    $scope.addBtnText = __("Add");
    $scope.saveBtnText = __("Save");
    $scope.saveAltText = __("Save Changes");
    $scope.submitBtnText = __("Submit");
    $scope.submitAltText = __("Submit Changes");
    $scope.resetBtnText = __("Reset");
    $scope.resetAltText = __("Reset Changes");
    $scope.cancelBtnText = __("Cancel");

    if ($attrs.pagingDivButtonsType == "Add") {
      saveButton('Add');
      cancelButton();
    } else if ($attrs.pagingDivButtonsType == "Submit") {
      saveButton('Submit');
      cancelButton();
    } else {
      saveButton();
      resetButton();
      cancelButton();
    }

    $scope.saveable = miqService.saveable;
    $scope.disabledClick = miqService.disabledClick;
  }

  var saveButton = function(type) {
    $scope.altText =  $scope.saveAltText;
    $scope.btnText = $scope.saveBtnText;

    if(type == "Add") {
      $scope.altText =  $scope.addBtnText;
      $scope.btnText = $scope.addBtnText;
    } else if(type == "Submit") {
      $scope.altText =  $scope.submitAltText;
      $scope.btnText = $scope.submitBtnText;
    }

    var disabledSaveHtml = '<button name="button" id="save_disabled" type="submit" class="btn btn-primary btn-disabled" ' +
      'alt={{altText}} title={{altText}} ng-click="disabledClick($event)" style="cursor:not-allowed" ' +
      'ng-show="!newRecord && !saveable(angularForm)">{{btnText}}</button>';
    var compiledDisabledSave = $compile(disabledSaveHtml)($scope);

    var enabledSaveHtml = '<button name="button" id="save_enabled" type="submit" class="btn btn-primary ng-hide" ' +
      'alt={{altText}} title={{altText}} ng-click="saveClicked($event, true)" ' +
      'ng-show="!newRecord && saveable(angularForm)">{{btnText}}</button>';
    var compiledEnabledSave = $compile(enabledSaveHtml)($scope);

    if (angular.element(document.getElementById('save_disabled')).length == 0) {
      angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledDisabledSave);
    }

    if (angular.element(document.getElementById('save_enabled')).length == 0) {
      angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledEnabledSave);
    }
  }

  var resetButton = function() {
    var resetHtml = '<button name="button" id="reset_enabled_disabled" type="submit" ' +
      'class="btn btn-default btn-disabled" alt={{resetAltText}} title={{resetAltText}} ' +
      'ng-class="{\'btn-disabled\': angularForm.$pristine}" ng-click="resetClicked()" ' +
      'ng-disabled="angularForm.$pristine" ng-hide="newRecord" disabled="disabled">{{resetBtnText}}</button>';
    var compiledReset = $compile(resetHtml)($scope);

    if (angular.element(document.getElementById('reset_enabled_disabled')).length == 0) {
      angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledReset);
    }
  }

  var cancelButton = function() {
    var cancelHtml = '<button name="button" id="cancel_enabled" type="submit" class="btn btn-default" alt={{cancelBtnText}} ' +
      'title={{cancelBtnText}} ng-click="cancelClicked($event)">{{cancelBtnText}}</button>';
    var compiledCancel = $compile(cancelHtml)($scope);

    if (angular.element(document.getElementById('cancel_enabled')).length == 0) {
      angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledCancel);
    }
  }
  init();
}]);
<commit_msg>Render buttons with $timeout<commit_after>ManageIQ.angular.app.controller('pagingDivButtonGroupController', ['$scope', 'miqService', '$compile', '$attrs', '$timeout', function($scope, miqService, $compile, $attrs, $timeout) {
  var init = function() {
    $scope.addBtnText = __("Add");
    $scope.saveBtnText = __("Save");
    $scope.saveAltText = __("Save Changes");
    $scope.submitBtnText = __("Submit");
    $scope.submitAltText = __("Submit Changes");
    $scope.resetBtnText = __("Reset");
    $scope.resetAltText = __("Reset Changes");
    $scope.cancelBtnText = __("Cancel");

    if ($attrs.pagingDivButtonsType == "Add") {
      saveButton('Add');
      cancelButton();
    } else if ($attrs.pagingDivButtonsType == "Submit") {
      saveButton('Submit');
      cancelButton();
    } else {
      saveButton();
      resetButton();
      cancelButton();
    }

    $scope.saveable = miqService.saveable;
    $scope.disabledClick = miqService.disabledClick;
  }

  var saveButton = function(type) {
    $scope.altText =  $scope.saveAltText;
    $scope.btnText = $scope.saveBtnText;

    if(type == "Add") {
      $scope.altText =  $scope.addBtnText;
      $scope.btnText = $scope.addBtnText;
    } else if(type == "Submit") {
      $scope.altText =  $scope.submitAltText;
      $scope.btnText = $scope.submitBtnText;
    }

    var disabledSaveHtml = '<button name="button" id="save_disabled" type="submit" class="btn btn-primary btn-disabled" ' +
      'alt={{altText}} title={{altText}} ng-click="disabledClick($event)" style="cursor:not-allowed" ' +
      'ng-show="!newRecord && !saveable(angularForm)">{{btnText}}</button>';
    var compiledDisabledSave = $compile(disabledSaveHtml)($scope);

    var enabledSaveHtml = '<button name="button" id="save_enabled" type="submit" class="btn btn-primary ng-hide" ' +
      'alt={{altText}} title={{altText}} ng-click="saveClicked($event, true)" ' +
      'ng-show="!newRecord && saveable(angularForm)">{{btnText}}</button>';
    var compiledEnabledSave = $compile(enabledSaveHtml)($scope);

    $timeout(function () {
      if (angular.element(document.getElementById('save_disabled')).length == 0) {
        angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledDisabledSave);
      }

      if (angular.element(document.getElementById('save_enabled')).length == 0) {
        angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledEnabledSave);
      }
    });
  };

  var resetButton = function() {
    var resetHtml = '<button name="button" id="reset_enabled_disabled" type="submit" ' +
      'class="btn btn-default btn-disabled" alt={{resetAltText}} title={{resetAltText}} ' +
      'ng-class="{\'btn-disabled\': angularForm.$pristine}" ng-click="resetClicked()" ' +
      'ng-disabled="angularForm.$pristine" ng-hide="newRecord" disabled="disabled">{{resetBtnText}}</button>';
    var compiledReset = $compile(resetHtml)($scope);

    $timeout(function () {
      if (angular.element(document.getElementById('reset_enabled_disabled')).length == 0) {
        angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledReset);
      }
    });
  };

  var cancelButton = function() {
    var cancelHtml = '<button name="button" id="cancel_enabled" type="submit" class="btn btn-default" alt={{cancelBtnText}} ' +
      'title={{cancelBtnText}} ng-click="cancelClicked($event)">{{cancelBtnText}}</button>';
    var compiledCancel = $compile(cancelHtml)($scope);

    $timeout(function () {
      if (angular.element(document.getElementById('cancel_enabled')).length == 0) {
        angular.element(document.getElementById($attrs.pagingDivButtonsId)).append(compiledCancel);
      }
    });
  };
  init();
}]);
<|endoftext|>