<commit_before>/**
* @author       Richard Davey <rich@photonstorm.com>
* @author       Mat Groves (@Doormat23)
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

Phaser.Renderer.Canvas.GameObjects.Image = {

    TYPES: [
        Phaser.GameObject.Image.prototype
    ],

    render: function (renderer, src)
    {
        var frame = src.frame;
        var source = frame.source;

        //  Skip render?

        if (!src.visible || !src.alpha || !src.renderable || !frame.cutWidth || !frame.cutHeight)
        {
            return;
        }

        //  Blend Mode

        if (src.blendMode !== renderer.currentBlendMode)
        {
            renderer.currentBlendMode = src.blendMode;
            renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode];
        }

        //  Alpha

        if (src.worldAlpha !== renderer.context.globalAlpha)
        {
            renderer.context.globalAlpha = src.worldAlpha;
        }

        //  Smoothing (should this be a Game Object, or Frame/Texture level property?)

        if (source.scaleMode !== renderer.currentScaleMode)
        {
            renderer.currentScaleMode = source.scaleMode;
            renderer.context[renderer.smoothProperty] = (source.scaleMode === Phaser.scaleModes.LINEAR);
        }

        var wt = src.worldTransform;

        var resolution = source.resolution / renderer.game.resolution;

        var dx = frame.x - (src.anchor.x * frame.width);
        var dy = frame.y - (src.anchor.y * frame.height);

        var tx = (wt.tx * renderer.game.resolution) + renderer.game.camera._shake.x;
        var ty = (wt.ty * renderer.game.resolution) + renderer.game.camera._shake.y;

        //  Round Pixels

        if (renderer.roundPixels)
        {
            tx |= 0;
            ty |= 0;
            dx |= 0;
            dy |= 0;
        }

        var cw = frame.cutWidth;
        var ch = frame.cutHeight;

        renderer.context.setTransform(wt.a, wt.b, wt.c, wt.d, tx, ty);

        renderer.context.drawImage(source.image, frame.cutX, frame.cutY, cw, ch, dx, dy, cw / resolution, ch / resolution);

        /*
        //  Move this to either the Renderer, or the Texture Manager, but not here (as it's repeated all over the place)
        if (src.texture.rotated)
        {
            var a = wt.a;
            var b = wt.b;
            var c = wt.c;
            var d = wt.d;
            var e = cw;

            // Offset before rotating
            tx = wt.c * ch + tx;
            ty = wt.d * ch + ty;

            // Rotate matrix by 90 degrees
            // We use precalculated values for sine and cosine of rad(90)
            wt.a = a * 6.123233995736766e-17 + -c;
            wt.b = b * 6.123233995736766e-17 + -d;
            wt.c = a + c * 6.123233995736766e-17;
            wt.d = b + d * 6.123233995736766e-17;

            // Update cropping dimensions.
            cw = ch;
            ch = e;
        }
        */

        /*
        if (src.tint !== 0xFFFFFF)
        {
            if (src.texture.requiresReTint || src.cachedTint !== src.tint)
            {
                src.tintedTexture = PIXI.CanvasTinter.getTintedTexture(src, src.tint);

                src.cachedTint = src.tint;
                src.texture.requiresReTint = false;
            }

            renderer.context.drawImage(src.tintedTexture, 0, 0, cw, ch, dx, dy, cw / resolution, ch / resolution);
        }
        else
        {
            var cx = src.texture.crop.x;
            var cy = src.texture.crop.y;

            renderer.context.drawImage(src.texture.baseTexture.source, cx, cy, cw, ch, dx, dy, cw / resolution, ch / resolution);
        }
        */

    }

};
<commit_msg>Updated to work with new Image properties.<commit_after>/**
* @author       Richard Davey <rich@photonstorm.com>
* @author       Mat Groves (@Doormat23)
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

Phaser.Renderer.Canvas.GameObjects.Image = {

    TYPES: [
        Phaser.GameObject.Image.prototype
    ],

    render: function (renderer, src)
    {
        var frame = src.frame;
        var source = frame.source;

        //  Skip rendering?

        if (src.skipRender || !src.visible || !src.alpha || !frame.cutWidth || !frame.cutHeight)
        {
            return;
        }

        //  Blend Mode

        if (src.blendMode !== renderer.currentBlendMode)
        {
            renderer.currentBlendMode = src.blendMode;
            renderer.context.globalCompositeOperation = renderer.blendModes[renderer.currentBlendMode];
        }

        //  Alpha (World Alpha?)

        if (src.alpha !== renderer.context.globalAlpha)
        {
            renderer.context.globalAlpha = src.alpha;
        }

        //  Smoothing (should this be a Game Object, or Frame / Texture level property?)

        if (source.scaleMode !== renderer.currentScaleMode)
        {
            renderer.currentScaleMode = source.scaleMode;
            renderer.context[renderer.smoothProperty] = (source.scaleMode === Phaser.scaleModes.LINEAR);
        }

        var wt = src.transform.world;

        var resolution = source.resolution / renderer.game.resolution;

        var dx = frame.x - (src.anchorX * frame.width);
        var dy = frame.y - (src.anchorY * frame.height);

        var tx = (wt.tx * renderer.game.resolution) + renderer.game.camera._shake.x;
        var ty = (wt.ty * renderer.game.resolution) + renderer.game.camera._shake.y;

        //  Round Pixels

        if (renderer.roundPixels)
        {
            tx |= 0;
            ty |= 0;
            dx |= 0;
            dy |= 0;
        }

        var cw = frame.cutWidth;
        var ch = frame.cutHeight;

        renderer.context.setTransform(wt.a, wt.b, wt.c, wt.d, tx, ty);

        renderer.context.drawImage(source.image, frame.cutX, frame.cutY, cw, ch, dx, dy, cw / resolution, ch / resolution);

        /*
        //  Move this to either the Renderer, or the Texture Manager, but not here (as it's repeated all over the place)
        if (src.texture.rotated)
        {
            var a = wt.a;
            var b = wt.b;
            var c = wt.c;
            var d = wt.d;
            var e = cw;

            // Offset before rotating
            tx = wt.c * ch + tx;
            ty = wt.d * ch + ty;

            // Rotate matrix by 90 degrees
            // We use precalculated values for sine and cosine of rad(90)
            wt.a = a * 6.123233995736766e-17 + -c;
            wt.b = b * 6.123233995736766e-17 + -d;
            wt.c = a + c * 6.123233995736766e-17;
            wt.d = b + d * 6.123233995736766e-17;

            // Update cropping dimensions.
            cw = ch;
            ch = e;
        }
        */

        /*
        if (src.tint !== 0xFFFFFF)
        {
            if (src.texture.requiresReTint || src.cachedTint !== src.tint)
            {
                src.tintedTexture = PIXI.CanvasTinter.getTintedTexture(src, src.tint);

                src.cachedTint = src.tint;
                src.texture.requiresReTint = false;
            }

            renderer.context.drawImage(src.tintedTexture, 0, 0, cw, ch, dx, dy, cw / resolution, ch / resolution);
        }
        else
        {
            var cx = src.texture.crop.x;
            var cy = src.texture.crop.y;

            renderer.context.drawImage(src.texture.baseTexture.source, cx, cy, cw, ch, dx, dy, cw / resolution, ch / resolution);
        }
        */

    }

};
<|endoftext|>


<commit_before>var _ = require('underscore');
var formatter = require('cdb/core/format');
var HistogramTitleView = require('./histogram_title_view');
var WidgetContent = require('../standard/widget_content_view');
var WidgetViewModel = require('../widget_content_model');
var HistogramChartView = require('./chart');
var placeholder = require('./placeholder.tpl');
var template = require('./content.tpl');
var AnimateValues = require('../animate_values.js');
var animationTemplate = require('./animation_template.tpl');

/**
 * Widget content view for a histogram
 */
module.exports = WidgetContent.extend({

  defaults: {
    chartHeight: 48 + 20 + 4
  },

  events: {
    'click .js-clear': '_clear',
    'click .js-zoom': '_zoom'
  },

  initialize: function() {
    this.model = this.options.dataModel;
    this.viewModel = new WidgetViewModel();
    this.lockedByUser = false;
    WidgetContent.prototype.initialize.call(this);
  },

  _initViews: function() {
    var titleView = new HistogramTitleView({
      viewModel: this.viewModel,
      dataModel: this.model
    });
    this.$('.js-title').html(titleView.render().el);
    this.addView(titleView);

    this._renderMiniChart();
    this._renderMainChart();
  },

  _initBinds: function() {
    this.model.once('change:data', this._onFirstLoad, this);
    this.model.bind('change:collapsed', function(mdl, isCollapsed) {
      this.$el.toggleClass('is-collapsed', !!isCollapsed);
    }, this);
  },

  _onFirstLoad: function() {
    this.render();
    this._storeBounds();

    this.model.bind('change', this._onChangeData, this);
    this.model._fetch();
  },

  _storeBounds: function() {
    var data = this.model.getData();
    if (data && data.length > 0) {
      this.firstData = data;
      this.start = data[0].start;
      this.end = data[data.length - 1].end;
      this.binsCount = data.length;
      this.model.set({ start: this.start, end: this.end, bins: this.binsCount });
    }
  },

  _isZoomed: function() {
    return this.viewModel.get('zoomed');
  },

  _onChangeData: function() {
    // if the action was initiated by the user
    // don't replace the stored data
    if (this.lockedByUser) {
      this.lockedByUser = false;
    } else {
      if (this._isZoomed()) {
        this.zoomedData = this.model.getData();
      } else {
        this.histogramChartView.generateShadowBars(this.firstData);
        this.originalData = this.model.getData();
      }

      this.histogramChartView.replaceData(this.model.getData());
    }

    if (this.unsettingRange) {
      this.unsettingRange = false;
      this.histogramChartView.replaceData(this.originalData);
      this.viewModel.set({ lo_index: null, hi_index: null });

      if (!this._isZoomed()) {
        this.histogramChartView.generateShadowBars(this.firstData);
      }

    } else {
      if (this._isZoomed() && !this.lockZoomedData) {
        this.lockZoomedData = true;
        this.zoomedData = this.model.getData();
      }
    }

    this._updateStats();
  },

  render: function() {
    this.clearSubViews();

    var data = this.model.getData();
    var isDataEmpty = _.isEmpty(data) || _.size(data) === 0;

    this.$el.html(
      template({
        title: this.model.get('title'),
        itemsCount: !isDataEmpty ? data.length : '-'
      })
    );

    if (isDataEmpty) {
      this._addPlaceholder();
    } else {
      this.originalData = this.model.getData();
      this._setupBindings();
      this._initViews();
    }

    return this;
  },

  _addPlaceholder: function() {
    this.$('.js-content').append(placeholder());
  },

  _renderMainChart: function() {
    this.histogramChartView = new HistogramChartView(({
      margin: { top: 4, right: 4, bottom: 4, left: 4 },
      hasShadowBards: true,
      hasHandles: true,
      hasAxisTip: true,
      width: this.canvasWidth,
      height: this.defaults.chartHeight,
      data: this.model.getData()
    }));

    this.$('.js-content').append(this.histogramChartView.el);
    this.addView(this.histogramChartView);

    this.histogramChartView.bind('range_updated', this._onRangeUpdated, this);
    this.histogramChartView.bind('on_brush_end', this._onBrushEnd, this);
    this.histogramChartView.bind('hover', this._onValueHover, this);
    this.histogramChartView.render().show();

    this._updateStats();
  },

  _renderMiniChart: function() {
    this.miniHistogramChartView = new HistogramChartView(({
      className: 'mini',
      margin: { top: 0, right: 0, bottom: 4, left: 4 },
      height: 40,
      showOnWidthChange: false,
      data: this.model.getData()
    }));

    this.addView(this.miniHistogramChartView);
    this.$('.js-content').append(this.miniHistogramChartView.el);
    this.miniHistogramChartView.bind('on_brush_end', this._onMiniRangeUpdated, this);
    this.miniHistogramChartView.render().hide();
  },

  _setupBindings: function() {
    this.viewModel.bind('change:zoomed', this._onChangeZoomed, this);
    this.viewModel.bind('change:zoom_enabled', this._onChangeZoomEnabled, this);
    this.viewModel.bind('change:filter_enabled', this._onChangeFilterEnabled, this);
    this.viewModel.bind('change:total', this._onChangeTotal, this);
    this.viewModel.bind('change:nulls', this._onChangeNulls, this);
    this.viewModel.bind('change:max',   this._onChangeMax, this);
    this.viewModel.bind('change:min',   this._onChangeMin, this);
    this.viewModel.bind('change:avg',   this._onChangeAvg, this);
  },

  _clearTooltip: function() {
    this.$(".js-tooltip").stop().hide();
  },

  _onValueHover: function(info) {
    var $tooltip = this.$(".js-tooltip");

    if (info && info.data) {
      $tooltip.css({ top: info.top, left: info.left });
      $tooltip.text(info.data);
      $tooltip.css({ left: info.left - $tooltip.width()/2 });
      $tooltip.fadeIn(70);
    } else {
      this._clearTooltip();
    }
  },

  _onMiniRangeUpdated: function(loBarIndex, hiBarIndex) {
    this.lockedByUser = false;
    this.lockZoomedData = false;

    this._clearTooltip();
    this.histogramChartView.removeSelection();

    var data = this.originalData;

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onBrushEnd: function(loBarIndex, hiBarIndex) {
    var data = this._getData();

    if (this._isZoomed()) {
      this.lockedByUser = true;
    }

    var properties = { filter_enabled: true, lo_index: loBarIndex, hi_index: hiBarIndex };

    if (!this.viewModel.get('zoomed')) {
      properties.zoom_enabled = true;
    }

    this.viewModel.set(properties);

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onRangeUpdated: function(loBarIndex, hiBarIndex) {

    var self = this;
    if (this.viewModel.get('zoomed')) {
      this.viewModel.set({ zoom_enabled: false, lo_index: loBarIndex, hi_index: hiBarIndex });
    } else {
      this.viewModel.set({ lo_index: loBarIndex, hi_index: hiBarIndex });
    }

    var updateStats = _.debounce(function() { self._updateStats(); }, 400);
    updateStats();
  },

  _getData: function() {
    var data = this.model.getData();

    if (this._isZoomed()) {
      data = this.zoomedData;
    }
    return data;
  },

  _onChangeFilterEnabled: function() {
    this.$(".js-filter").toggleClass('is-hidden', !this.viewModel.get('filter_enabled'));
  },

  _onChangeZoomEnabled: function() {
    this.$(".js-zoom").toggleClass('is-hidden', !this.viewModel.get('zoom_enabled'));
  },

  _changeHeaderValue: function(className, what, suffix) {
    if (this.viewModel.get(what) === undefined) {
      this.$(className).text('0 ' + suffix);
      return;
    }

    this._addTitleForValue(className, what, suffix);

    var animator = new AnimateValues({
      el: this.$el
    });

    animator.animateValue(this.viewModel, what, className, animationTemplate, {
      formatter: formatter.formatNumber,
      templateData: { suffix: " " + suffix }
    });
  },

  _onChangeNulls: function() {
    this._changeHeaderValue('.js-nulls', 'nulls', 'NULL ROWS');
  },

  _onChangeTotal: function() {
    this._changeHeaderValue('.js-val', 'total', 'SELECTED');
  },

  _onChangeMax: function() {
    this._changeHeaderValue('.js-max', 'max', 'MAX');
  },

  _onChangeMin: function() {
    this._changeHeaderValue('.js-min', 'min', 'MIN');
  },

  _onChangeAvg: function() {
    this._changeHeaderValue('.js-avg', 'avg', 'AVG');
  },

  _addTitleForValue: function(className, what, unit) {
    this.$(className).attr('title', this._formatNumberWithCommas(this.viewModel.get(what).toFixed(2)) + ' ' + unit);
  },

  _formatNumberWithCommas: function(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  },

  _updateStats: function() {
    var data = this.originalData;

    if (this._isZoomed()) {
      data = this.zoomedData;
    }

    var nulls = this.model.get('nulls');

    var min, max;

    if (data && data.length) {

      var loBarIndex = this.viewModel.get('lo_index') || 0;
      var hiBarIndex = this.viewModel.get('hi_index') || data.length;

      var sum = this._calcSum(data, loBarIndex, hiBarIndex);
      var avg = this._calcAvg(data, loBarIndex, hiBarIndex);

      if (loBarIndex >= 0 && loBarIndex < data.length) {
        min = data[loBarIndex].start;
      }

      if (hiBarIndex >= 0 && hiBarIndex - 1 < data.length) {
        max = data[hiBarIndex - 1].end;
      }

      this.viewModel.set({ total: sum, nulls: nulls, min: min, max: max, avg: avg });
    }
  },

  _calcAvg: function(data, start, end) {

    var selectedData = data.slice(start, end);

    var total = this._calcSum(data, start, end, total);

    if (!total) {
      return 0;
    }

    var area = _.reduce(selectedData, function(memo, d) {
      return (d.avg && d.freq) ? (d.avg * d.freq) + memo : memo;
    }, 0);

    return area / total;
  },

  _calcSum: function(data, start, end) {
    return _.reduce(data.slice(start, end), function(memo, d) {
      return d.freq + memo;
    }, 0);
  },

  _onChangeZoomed: function() {
    if (this.viewModel.get('zoomed')) {
      this._onZoomIn();
    } else {
      this._onZoomOut();
    }
  },

  _onZoomIn: function() {
    this._showMiniRange();
    this.histogramChartView.expand(20);

    this.model.set({ start: null, end: null, bins: null, own_filter: 1 });
    this.model._fetch();
    this.lockedByUser = false;
  },

  _zoom: function() {
    this.lockedByUser = true;
    this.viewModel.set({ zoomed: true, zoom_enabled: false });
    this.histogramChartView.removeSelection();
  },

  _onZoomOut: function() {
    this.lockedByUser   = true;
    this.lockZoomedData = false;
    this.unsettingRange = true;

    this.model.set({ start: this.start, end: this.end, bins: this.binsCount, own_filter: null });

    this.viewModel.set({ zoom_enabled: false, filter_enabled: false, lo_index: null, hi_index: null });

    this.filter.unsetRange();

    this.histogramChartView.contract(this.defaults.chartHeight);
    this.histogramChartView.resetIndexes();

    this.miniHistogramChartView.hide();
  },

  _showMiniRange: function() {
    var data = this.model.getData();

    var loBarIndex = this.viewModel.get('lo_index');
    var hiBarIndex = this.viewModel.get('hi_index');

    this.miniHistogramChartView.selectRange(loBarIndex, hiBarIndex);
    this.miniHistogramChartView.show();
  },

  _clear: function() {
    this.histogramChartView.removeSelection();
    this.viewModel.set({ zoomed: false, zoom_enabled: false });
    this.viewModel.trigger('change:zoomed');
  }
});
<commit_msg>Little refactor<commit_after>var _ = require('underscore');
var formatter = require('cdb/core/format');
var HistogramTitleView = require('./histogram_title_view');
var WidgetContent = require('../standard/widget_content_view');
var WidgetViewModel = require('../widget_content_model');
var HistogramChartView = require('./chart');
var placeholder = require('./placeholder.tpl');
var template = require('./content.tpl');
var AnimateValues = require('../animate_values.js');
var animationTemplate = require('./animation_template.tpl');

/**
 * Widget content view for a histogram
 */
module.exports = WidgetContent.extend({

  defaults: {
    chartHeight: 48 + 20 + 4
  },

  events: {
    'click .js-clear': '_clear',
    'click .js-zoom': '_zoom'
  },

  initialize: function() {
    this.model = this.options.dataModel;
    this.viewModel = new WidgetViewModel();
    this.lockedByUser = false;
    WidgetContent.prototype.initialize.call(this);
  },

  _initViews: function() {
    var titleView = new HistogramTitleView({
      viewModel: this.viewModel,
      dataModel: this.model
    });
    this.$('.js-title').html(titleView.render().el);
    this.addView(titleView);

    this._renderMiniChart();
    this._renderMainChart();
  },

  _initBinds: function() {
    this.model.once('change:data', this._onFirstLoad, this);
    this.model.bind('change:collapsed', function(mdl, isCollapsed) {
      this.$el.toggleClass('is-collapsed', !!isCollapsed);
    }, this);
  },

  _onFirstLoad: function() {
    this.render();
    this._storeBounds();

    this.model.bind('change', this._onChangeData, this);
    this.model._fetch();
  },

  _storeBounds: function() {
    var data = this.model.getData();
    if (data && data.length > 0) {
      this.initialData = data;
      this.start = data[0].start;
      this.end = data[data.length - 1].end;
      this.binsCount = data.length;
      this.model.set({ start: this.start, end: this.end, bins: this.binsCount });
    }
  },

  _isZoomed: function() {
    return this.viewModel.get('zoomed');
  },

  _onChangeData: function() {
    // if the action was initiated by the user
    // don't replace the stored data
    if (this.lockedByUser) {
      this.lockedByUser = false;
    } else {
      if (this._isZoomed()) {
        this.zoomedData = this.model.getData();
      } else {
        this.histogramChartView.generateShadowBars(this.initialData);
        this.originalData = this.model.getData();
      }
      this.histogramChartView.replaceData(this.model.getData());
    }

    if (this.unsettingRange) {
      this._unsetRange();
    } else {
      if (this._isZoomed() && !this.lockZoomedData) {
        this.lockZoomedData = true;
        this.zoomedData = this.model.getData();
      }
    }

    this._updateStats();
  },

  render: function() {
    this.clearSubViews();

    var data = this.model.getData();
    var isDataEmpty = _.isEmpty(data) || _.size(data) === 0;

    this.$el.html(
      template({
        title: this.model.get('title'),
        itemsCount: !isDataEmpty ? data.length : '-'
      })
    );

    if (isDataEmpty) {
      this._addPlaceholder();
    } else {
      this.originalData = this.model.getData();
      this._setupBindings();
      this._initViews();
    }

    return this;
  },

  _unsetRange: function() {
    this.unsettingRange = false;
    this.histogramChartView.replaceData(this.originalData);
    this.viewModel.set({ lo_index: null, hi_index: null });

    if (!this._isZoomed()) {
      this.histogramChartView.generateShadowBars(this.initialData);
    }
  },

  _addPlaceholder: function() {
    this.$('.js-content').append(placeholder());
  },

  _renderMainChart: function() {
    this.histogramChartView = new HistogramChartView(({
      margin: { top: 4, right: 4, bottom: 4, left: 4 },
      hasShadowBards: true,
      hasHandles: true,
      hasAxisTip: true,
      width: this.canvasWidth,
      height: this.defaults.chartHeight,
      data: this.model.getData()
    }));

    this.$('.js-content').append(this.histogramChartView.el);
    this.addView(this.histogramChartView);

    this.histogramChartView.bind('range_updated', this._onRangeUpdated, this);
    this.histogramChartView.bind('on_brush_end', this._onBrushEnd, this);
    this.histogramChartView.bind('hover', this._onValueHover, this);
    this.histogramChartView.render().show();

    this._updateStats();
  },

  _renderMiniChart: function() {
    this.miniHistogramChartView = new HistogramChartView(({
      className: 'mini',
      margin: { top: 0, right: 0, bottom: 4, left: 4 },
      height: 40,
      showOnWidthChange: false,
      data: this.model.getData()
    }));

    this.addView(this.miniHistogramChartView);
    this.$('.js-content').append(this.miniHistogramChartView.el);
    this.miniHistogramChartView.bind('on_brush_end', this._onMiniRangeUpdated, this);
    this.miniHistogramChartView.render().hide();
  },

  _setupBindings: function() {
    this.viewModel.bind('change:zoomed', this._onChangeZoomed, this);
    this.viewModel.bind('change:zoom_enabled', this._onChangeZoomEnabled, this);
    this.viewModel.bind('change:filter_enabled', this._onChangeFilterEnabled, this);
    this.viewModel.bind('change:total', this._onChangeTotal, this);
    this.viewModel.bind('change:nulls', this._onChangeNulls, this);
    this.viewModel.bind('change:max',   this._onChangeMax, this);
    this.viewModel.bind('change:min',   this._onChangeMin, this);
    this.viewModel.bind('change:avg',   this._onChangeAvg, this);
  },

  _clearTooltip: function() {
    this.$(".js-tooltip").stop().hide();
  },

  _onValueHover: function(info) {
    var $tooltip = this.$(".js-tooltip");

    if (info && info.data) {
      $tooltip.css({ top: info.top, left: info.left });
      $tooltip.text(info.data);
      $tooltip.css({ left: info.left - $tooltip.width()/2 });
      $tooltip.fadeIn(70);
    } else {
      this._clearTooltip();
    }
  },

  _onMiniRangeUpdated: function(loBarIndex, hiBarIndex) {
    this.lockedByUser = false;
    this.lockZoomedData = false;

    this._clearTooltip();
    this.histogramChartView.removeSelection();

    var data = this.originalData;

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onBrushEnd: function(loBarIndex, hiBarIndex) {
    var data = this._getData();

    if (this._isZoomed()) {
      this.lockedByUser = true;
    }

    var properties = { filter_enabled: true, lo_index: loBarIndex, hi_index: hiBarIndex };

    if (!this.viewModel.get('zoomed')) {
      properties.zoom_enabled = true;
    }

    this.viewModel.set(properties);

    if (loBarIndex >= 0 && loBarIndex < data.length && (hiBarIndex - 1) >= 0 && (hiBarIndex - 1) < data.length) {
      this.filter.setRange(
        data[loBarIndex].start,
        data[hiBarIndex - 1].end
      );
      this._updateStats();
    } else {
      console.error('Error accessing array bounds', loBarIndex, hiBarIndex, data);
    }
  },

  _onRangeUpdated: function(loBarIndex, hiBarIndex) {

    var self = this;
    if (this.viewModel.get('zoomed')) {
      this.viewModel.set({ zoom_enabled: false, lo_index: loBarIndex, hi_index: hiBarIndex });
    } else {
      this.viewModel.set({ lo_index: loBarIndex, hi_index: hiBarIndex });
    }

    var updateStats = _.debounce(function() { self._updateStats(); }, 400);
    updateStats();
  },

  _getData: function() {
    var data = this.model.getData();

    if (this._isZoomed()) {
      data = this.zoomedData;
    }
    return data;
  },

  _onChangeFilterEnabled: function() {
    this.$(".js-filter").toggleClass('is-hidden', !this.viewModel.get('filter_enabled'));
  },

  _onChangeZoomEnabled: function() {
    this.$(".js-zoom").toggleClass('is-hidden', !this.viewModel.get('zoom_enabled'));
  },

  _changeHeaderValue: function(className, what, suffix) {
    if (this.viewModel.get(what) === undefined) {
      this.$(className).text('0 ' + suffix);
      return;
    }

    this._addTitleForValue(className, what, suffix);

    var animator = new AnimateValues({
      el: this.$el
    });

    animator.animateValue(this.viewModel, what, className, animationTemplate, {
      formatter: formatter.formatNumber,
      templateData: { suffix: " " + suffix }
    });
  },

  _onChangeNulls: function() {
    this._changeHeaderValue('.js-nulls', 'nulls', 'NULL ROWS');
  },

  _onChangeTotal: function() {
    this._changeHeaderValue('.js-val', 'total', 'SELECTED');
  },

  _onChangeMax: function() {
    this._changeHeaderValue('.js-max', 'max', 'MAX');
  },

  _onChangeMin: function() {
    this._changeHeaderValue('.js-min', 'min', 'MIN');
  },

  _onChangeAvg: function() {
    this._changeHeaderValue('.js-avg', 'avg', 'AVG');
  },

  _addTitleForValue: function(className, what, unit) {
    this.$(className).attr('title', this._formatNumberWithCommas(this.viewModel.get(what).toFixed(2)) + ' ' + unit);
  },

  _formatNumberWithCommas: function(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  },

  _updateStats: function() {
    var data = this.originalData;

    if (this._isZoomed()) {
      data = this.zoomedData;
    }

    var nulls = this.model.get('nulls');

    var min, max;

    if (data && data.length) {

      var loBarIndex = this.viewModel.get('lo_index') || 0;
      var hiBarIndex = this.viewModel.get('hi_index') || data.length;

      var sum = this._calcSum(data, loBarIndex, hiBarIndex);
      var avg = this._calcAvg(data, loBarIndex, hiBarIndex);

      if (loBarIndex >= 0 && loBarIndex < data.length) {
        min = data[loBarIndex].start;
      }

      if (hiBarIndex >= 0 && hiBarIndex - 1 < data.length) {
        max = data[hiBarIndex - 1].end;
      }

      this.viewModel.set({ total: sum, nulls: nulls, min: min, max: max, avg: avg });
    }
  },

  _calcAvg: function(data, start, end) {

    var selectedData = data.slice(start, end);

    var total = this._calcSum(data, start, end, total);

    if (!total) {
      return 0;
    }

    var area = _.reduce(selectedData, function(memo, d) {
      return (d.avg && d.freq) ? (d.avg * d.freq) + memo : memo;
    }, 0);

    return area / total;
  },

  _calcSum: function(data, start, end) {
    return _.reduce(data.slice(start, end), function(memo, d) {
      return d.freq + memo;
    }, 0);
  },

  _onChangeZoomed: function() {
    if (this.viewModel.get('zoomed')) {
      this._onZoomIn();
    } else {
      this._onZoomOut();
    }
  },

  _onZoomIn: function() {
    this._showMiniRange();
    this.histogramChartView.expand(20);

    this.model.set({ start: null, end: null, bins: null, own_filter: 1 });
    this.model._fetch();
    this.lockedByUser = false;
  },

  _zoom: function() {
    this.lockedByUser = true;
    this.viewModel.set({ zoomed: true, zoom_enabled: false });
    this.histogramChartView.removeSelection();
  },

  _onZoomOut: function() {
    this.lockedByUser   = true;
    this.lockZoomedData = false;
    this.unsettingRange = true;

    this.model.set({ start: this.start, end: this.end, bins: this.binsCount, own_filter: null });

    this.viewModel.set({ zoom_enabled: false, filter_enabled: false, lo_index: null, hi_index: null });

    this.filter.unsetRange();

    this.histogramChartView.contract(this.defaults.chartHeight);
    this.histogramChartView.resetIndexes();

    this.miniHistogramChartView.hide();
  },

  _showMiniRange: function() {
    var data = this.model.getData();

    var loBarIndex = this.viewModel.get('lo_index');
    var hiBarIndex = this.viewModel.get('hi_index');

    this.miniHistogramChartView.selectRange(loBarIndex, hiBarIndex);
    this.miniHistogramChartView.show();
  },

  _clear: function() {
    this.histogramChartView.removeSelection();
    this.viewModel.set({ zoomed: false, zoom_enabled: false });
    this.viewModel.trigger('change:zoomed');
  }
});
<|endoftext|>


<commit_before>import 'core-js/stable';
import 'regenerator-runtime/runtime';
import Vue from 'vue';
import Vuex from 'vuex';
import {BootstrapVue} from 'bootstrap-vue';
import {library} from '@fortawesome/fontawesome-svg-core';
import {
  FontAwesomeIcon,
  FontAwesomeLayers,
} from '@fortawesome/vue-fontawesome';
import VueMq from 'vue-mq';

import {
  faUser,
  faEnvelope,
  faSearch,
  faSignOutAlt,
  faHome,
  faPaw,
  faBookmark,
  faExclamationTriangle,
  faGraduationCap,
  faBars,
} from '@fortawesome/free-solid-svg-icons';

import {
  faEdit,
  faCreditCard,
  faCalendarCheck,
  faSquare,
} from '@fortawesome/free-regular-svg-icons';

import '../css/bootstrap-theming.scss';
import 'bootstrap-vue/dist/bootstrap-vue.css';

library.add(faUser);
library.add(faEnvelope);
library.add(faSearch);
library.add(faSignOutAlt);
library.add(faHome);
library.add(faPaw);
library.add(faGraduationCap);
library.add(faEdit);
library.add(faCreditCard);
library.add(faCalendarCheck);
library.add(faBookmark);
library.add(faExclamationTriangle);
library.add(faSquare);
library.add(faBars);

// fontawesome 5
Vue.component('font-awesome-icon', FontAwesomeIcon);
Vue.component('font-awesome-layers', FontAwesomeLayers);

// vuex
Vue.use(Vuex);
Vue.use(BootstrapVue);

// vue-mq (media queries)
Vue.use(VueMq, {
  breakpoints: {
    // breakpoints == min-widths of next size
    mobile: 768, // tablet begins 768px
    tablet: 992, // desktop begins 992px
    desktop: Infinity,
  },
});

const store = new Vuex.Store({
  state: {
    user: JSON.parse(document.getElementById('user').innerHTML),
    staticUrl: JSON.parse(document.getElementById('static_url').innerHTML),
    disableActions: JSON.parse(document.getElementById('disable_actions').innerHTML),
  },
});

const vueConf = {
  el: '#vue_root',
  created: function() {
    document.title = 'MyUW: ' + store.state['pageTitle'];
    document.getElementById('vue_root').hidden = false;
  },
  store: store,
};
export {Vue, vueConf};
<commit_msg>Fixes lint error.<commit_after>import 'core-js/stable';
import 'regenerator-runtime/runtime';
import Vue from 'vue';
import Vuex from 'vuex';
import { BootstrapVue } from 'bootstrap-vue';
import { library } from '@fortawesome/fontawesome-svg-core';
import {
  FontAwesomeIcon,
  FontAwesomeLayers,
} from '@fortawesome/vue-fontawesome';
import VueMq from 'vue-mq';

import {
  faUser,
  faEnvelope,
  faSearch,
  faSignOutAlt,
  faHome,
  faPaw,
  faBookmark,
  faExclamationTriangle,
  faGraduationCap,
  faBars,
} from '@fortawesome/free-solid-svg-icons';

import {
  faEdit,
  faCreditCard,
  faCalendarCheck,
  faSquare,
} from '@fortawesome/free-regular-svg-icons';

import '../css/bootstrap-theming.scss';
import 'bootstrap-vue/dist/bootstrap-vue.css';

library.add(faUser);
library.add(faEnvelope);
library.add(faSearch);
library.add(faSignOutAlt);
library.add(faHome);
library.add(faPaw);
library.add(faGraduationCap);
library.add(faEdit);
library.add(faCreditCard);
library.add(faCalendarCheck);
library.add(faBookmark);
library.add(faExclamationTriangle);
library.add(faSquare);
library.add(faBars);

// fontawesome 5
Vue.component('font-awesome-icon', FontAwesomeIcon);
Vue.component('font-awesome-layers', FontAwesomeLayers);

// vuex
Vue.use(Vuex);
Vue.use(BootstrapVue);

// vue-mq (media queries)
Vue.use(VueMq, {
  breakpoints: {
    // breakpoints == min-widths of next size
    mobile: 768, // tablet begins 768px
    tablet: 992, // desktop begins 992px
    desktop: Infinity,
  },
});

const store = new Vuex.Store({
  state: {
    user: JSON.parse(document.getElementById('user').innerHTML),
    staticUrl: JSON.parse(document.getElementById('static_url').innerHTML),
    disableActions: JSON.parse(
      document.getElementById('disable_actions').innerHTML
    ),
  },
});

const vueConf = {
  el: '#vue_root',
  created: function () {
    document.title = 'MyUW: ' + store.state['pageTitle'];
    document.getElementById('vue_root').hidden = false;
  },
  store: store,
};
export { Vue, vueConf };
<|endoftext|>


<commit_before>/* global WeatherProvider, WeatherObject, SunCalc */

/* Magic Mirror
 * Module: Weather
 * Provider: weather.gov
 * https://weather-gov.github.io/api/general-faqs
 *
 * Original by Vince Peri
 * MIT Licensed.
 *
 * This class is a provider for weather.gov.
 * Note that this is only for US locations (lat and lon) and does not require an API key
 * Since it is free, there are some items missing - like sunrise, sunset
 */

WeatherProvider.register("weathergov", {
	// Set the name of the provider.
	// This isn't strictly necessary, since it will fallback to the provider identifier
	// But for debugging (and future alerts) it would be nice to have the real name.
	providerName: "Weather.gov",

	// Flag all needed URLs availability
	configURLs: false,

	//This API has multiple urls involved
	forecastURL: "tbd",
	forecastHourlyURL: "tbd",
	forecastGridDataURL: "tbd",
	observationStationsURL: "tbd",
	stationObsURL: "tbd",

	// Called to set the config, this config is the same as the weather module's config.
	setConfig: function (config) {
		this.config = config;
		(this.config.apiBase = "https://api.weather.gov"), this.fetchWxGovURLs(this.config);
	},

	// Called when the weather provider is about to start.
	start: function () {
		Log.info(`Weather provider: ${this.providerName} started.`);
	},

	// This returns the name of the fetched location or an empty string.
	fetchedLocation: function () {
		return this.fetchedLocationName || "";
	},

	// Overwrite the fetchCurrentWeather method.
	fetchCurrentWeather() {
		if (!this.configURLs) {
			Log.info("fetch wx waiting on config URLs");
			return;
		}
		this.fetchData(this.stationObsURL)
			.then((data) => {
				if (!data || !data.properties) {
					// Did not receive usable new data.
					return;
				}
				const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties);
				this.setCurrentWeather(currentWeather);
			})
			.catch(function (request) {
				Log.error("Could not load station obs data ... ", request);
			})
			.finally(() => this.updateAvailable());
	},

	// Overwrite the fetchWeatherForecast method.
	fetchWeatherForecast() {
		if (!this.configURLs) {
			Log.info("fetch wx waiting on config URLs");
			return;
		}
		this.fetchData(this.forecastURL)
			.then((data) => {
				if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
					// Did not receive usable new data.
					return;
				}
				const forecast = this.generateWeatherObjectsFromForecast(data.properties.periods);
				this.setWeatherForecast(forecast);
			})
			.catch(function (request) {
				Log.error("Could not load forecast hourly data ... ", request);
			})
			.finally(() => this.updateAvailable());
	},

	/** Weather.gov Specific Methods - These are not part of the default provider methods */

	/*
	 * Get specific URLs
	 */
	fetchWxGovURLs(config) {
		this.fetchData(`${config.apiBase}/points/${config.lat},${config.lon}`)
			.then((data) => {
				if (!data || !data.properties) {
					// points URL did not respond with usable data.
					return;
				}
				this.fetchedLocationName = data.properties.relativeLocation.properties.city + ", " + data.properties.relativeLocation.properties.state;
				Log.log("Forecast location is " + this.fetchedLocationName);
				this.forecastURL = data.properties.forecast;
				this.forecastHourlyURL = data.properties.forecastHourly;
				this.forecastGridDataURL = data.properties.forecastGridData;
				this.observationStationsURL = data.properties.observationStations;
				// with this URL, we chain another promise for the station obs URL
				return this.fetchData(data.properties.observationStations);
			})
			.then((obsData) => {
				if (!obsData || !obsData.features) {
					// obs station URL did not respond with usable data.
					return;
				}
				this.stationObsURL = obsData.features[0].id + "/observations/latest";
			})
			.catch((err) => {
				Log.error(err);
			})
			.finally(() => {
				// excellent, let's fetch some actual wx data
				this.configURLs = true;
				this.fetchCurrentWeather();
			});
	},

	/*
	 * Generate a WeatherObject based on currentWeatherInformation
	 * Weather.gov API uses specific units; API does not include choice of units
	 * ... object needs data in units based on config!
	 */
	generateWeatherObjectFromCurrentWeather(currentWeatherData) {
		const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);

		currentWeather.date = moment(currentWeatherData.timestamp);
		currentWeather.temperature = this.convertTemp(currentWeatherData.temperature.value);
		currentWeather.windSpeed = this.convertSpeed(currentWeatherData.windSpeed.value);
		currentWeather.windDirection = currentWeatherData.windDirection.value;
		currentWeather.minTemperature = this.convertTemp(currentWeatherData.minTemperatureLast24Hours.value);
		currentWeather.maxTemperature = this.convertTemp(currentWeatherData.maxTemperatureLast24Hours.value);
		currentWeather.humidity = Math.round(currentWeatherData.relativeHumidity.value);
		currentWeather.rain = null;
		currentWeather.snow = null;
		currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value);
		currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value);

		let isDaytime = true;
		if (currentWeatherData.icon.includes("day")) {
			isDaytime = true;
		} else {
			isDaytime = false;
		}
		currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, isDaytime);

		// determine the sunrise/sunset times - not supplied in weather.gov data
		let times = this.calcAstroData(this.config.lat, this.config.lon);
		currentWeather.sunrise = times[0];
		currentWeather.sunset = times[1];

		return currentWeather;
	},

	/*
	 * Generate WeatherObjects based on forecast information
	 */
	generateWeatherObjectsFromForecast(forecasts) {
		return this.fetchForecastDaily(forecasts);
	},

	/*
	 * fetch forecast information for daily forecast.
	 */
	fetchForecastDaily(forecasts) {
		// initial variable declaration
		const days = [];
		// variables for temperature range and rain
		let minTemp = [];
		let maxTemp = [];
		// variable for date
		let date = "";
		let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
		weather.precipitation = 0;

		for (const forecast of forecasts) {
			if (date !== moment(forecast.startTime).format("YYYY-MM-DD")) {
				// calculate minimum/maximum temperature, specify rain amount
				weather.minTemperature = Math.min.apply(null, minTemp);
				weather.maxTemperature = Math.max.apply(null, maxTemp);

				// push weather information to days array
				days.push(weather);
				// create new weather-object
				weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);

				minTemp = [];
				maxTemp = [];
				weather.precipitation = 0;

				// set new date
				date = moment(forecast.startTime).format("YYYY-MM-DD");

				// specify date
				weather.date = moment(forecast.startTime);

				// use the forecast isDayTime attribute to help build the weatherType label
				weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
			}

			if (moment(forecast.startTime).format("H") >= 8 && moment(forecast.startTime).format("H") <= 17) {
				weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
			}

			// the same day as before
			// add values from forecast to corresponding variables
			minTemp.push(forecast.temperature);
			maxTemp.push(forecast.temperature);
		}

		// last day
		// calculate minimum/maximum temperature
		weather.minTemperature = Math.min.apply(null, minTemp);
		weather.maxTemperature = Math.max.apply(null, maxTemp);

		// push weather information to days array
		days.push(weather);
		return days.slice(1);
	},

	/*
	 * Unit conversions
	 */
	// conversion to fahrenheit
	convertTemp(temp) {
		if (this.config.tempUnits === "imperial") {
			return (9 / 5) * temp + 32;
		} else {
			return temp;
		}
	},
	// conversion to mph or kmh
	convertSpeed(metSec) {
		if (this.config.windUnits === "imperial") {
			return metSec * 2.23694;
		} else {
			if(this.config.useKmh) {
				return metSec * 3.6;
			} else {
				return metSec;
			}
		}
	},
	// conversion to inches
	convertLength(meters) {
		if (this.config.units === "imperial") {
			return meters * 39.3701;
		} else {
			return meters;
		}
	},

	/*
	 * Calculate the astronomical data
	 */
	calcAstroData(lat, lon) {
		const sunTimes = [];

		// determine the sunrise/sunset times
		let times = SunCalc.getTimes(new Date(), lat, lon);
		sunTimes.push(moment(times.sunrise, "X"));
		sunTimes.push(moment(times.sunset, "X"));

		return sunTimes;
	},

	/*
	 * Convert the icons to a more usable name.
	 */
	convertWeatherType(weatherType, isDaytime) {
		//https://w1.weather.gov/xml/current_obs/weather.php
		// There are way too many types to create, so lets just look for certain strings

		if (weatherType.includes("Cloudy") || weatherType.includes("Partly")) {
			if (isDaytime) {
				return "day-cloudy";
			}

			return "night-cloudy";
		} else if (weatherType.includes("Overcast")) {
			if (isDaytime) {
				return "cloudy";
			}

			return "night-cloudy";
		} else if (weatherType.includes("Freezing") || weatherType.includes("Ice")) {
			return "rain-mix";
		} else if (weatherType.includes("Snow")) {
			if (isDaytime) {
				return "snow";
			}

			return "night-snow";
		} else if (weatherType.includes("Thunderstorm")) {
			if (isDaytime) {
				return "thunderstorm";
			}

			return "night-thunderstorm";
		} else if (weatherType.includes("Showers")) {
			if (isDaytime) {
				return "showers";
			}

			return "night-showers";
		} else if (weatherType.includes("Rain") || weatherType.includes("Drizzle")) {
			if (isDaytime) {
				return "rain";
			}

			return "night-rain";
		} else if (weatherType.includes("Breezy") || weatherType.includes("Windy")) {
			if (isDaytime) {
				return "cloudy-windy";
			}

			return "night-alt-cloudy-windy";
		} else if (weatherType.includes("Fair") || weatherType.includes("Clear") || weatherType.includes("Few") || weatherType.includes("Sunny")) {
			if (isDaytime) {
				return "day-sunny";
			}

			return "night-clear";
		} else if (weatherType.includes("Dust") || weatherType.includes("Sand")) {
			return "dust";
		} else if (weatherType.includes("Fog")) {
			return "fog";
		} else if (weatherType.includes("Smoke")) {
			return "smoke";
		} else if (weatherType.includes("Haze")) {
			return "day-haze";
		}

		return null;
	},

	/*
	Convert the direction into Degrees
	*/
	convertWindDirection(windDirection) {
		const windCardinals = {
			N: 0,
			NNE: 22,
			NE: 45,
			ENE: 67,
			E: 90,
			ESE: 112,
			SE: 135,
			SSE: 157,
			S: 180,
			SSW: 202,
			SW: 225,
			WSW: 247,
			W: 270,
			WNW: 292,
			NW: 315,
			NNW: 337
		};

		return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
	}
});
<commit_msg>Fix prettier issue.<commit_after>/* global WeatherProvider, WeatherObject, SunCalc */

/* Magic Mirror
 * Module: Weather
 * Provider: weather.gov
 * https://weather-gov.github.io/api/general-faqs
 *
 * Original by Vince Peri
 * MIT Licensed.
 *
 * This class is a provider for weather.gov.
 * Note that this is only for US locations (lat and lon) and does not require an API key
 * Since it is free, there are some items missing - like sunrise, sunset
 */

WeatherProvider.register("weathergov", {
	// Set the name of the provider.
	// This isn't strictly necessary, since it will fallback to the provider identifier
	// But for debugging (and future alerts) it would be nice to have the real name.
	providerName: "Weather.gov",

	// Flag all needed URLs availability
	configURLs: false,

	//This API has multiple urls involved
	forecastURL: "tbd",
	forecastHourlyURL: "tbd",
	forecastGridDataURL: "tbd",
	observationStationsURL: "tbd",
	stationObsURL: "tbd",

	// Called to set the config, this config is the same as the weather module's config.
	setConfig: function (config) {
		this.config = config;
		(this.config.apiBase = "https://api.weather.gov"), this.fetchWxGovURLs(this.config);
	},

	// Called when the weather provider is about to start.
	start: function () {
		Log.info(`Weather provider: ${this.providerName} started.`);
	},

	// This returns the name of the fetched location or an empty string.
	fetchedLocation: function () {
		return this.fetchedLocationName || "";
	},

	// Overwrite the fetchCurrentWeather method.
	fetchCurrentWeather() {
		if (!this.configURLs) {
			Log.info("fetch wx waiting on config URLs");
			return;
		}
		this.fetchData(this.stationObsURL)
			.then((data) => {
				if (!data || !data.properties) {
					// Did not receive usable new data.
					return;
				}
				const currentWeather = this.generateWeatherObjectFromCurrentWeather(data.properties);
				this.setCurrentWeather(currentWeather);
			})
			.catch(function (request) {
				Log.error("Could not load station obs data ... ", request);
			})
			.finally(() => this.updateAvailable());
	},

	// Overwrite the fetchWeatherForecast method.
	fetchWeatherForecast() {
		if (!this.configURLs) {
			Log.info("fetch wx waiting on config URLs");
			return;
		}
		this.fetchData(this.forecastURL)
			.then((data) => {
				if (!data || !data.properties || !data.properties.periods || !data.properties.periods.length) {
					// Did not receive usable new data.
					return;
				}
				const forecast = this.generateWeatherObjectsFromForecast(data.properties.periods);
				this.setWeatherForecast(forecast);
			})
			.catch(function (request) {
				Log.error("Could not load forecast hourly data ... ", request);
			})
			.finally(() => this.updateAvailable());
	},

	/** Weather.gov Specific Methods - These are not part of the default provider methods */

	/*
	 * Get specific URLs
	 */
	fetchWxGovURLs(config) {
		this.fetchData(`${config.apiBase}/points/${config.lat},${config.lon}`)
			.then((data) => {
				if (!data || !data.properties) {
					// points URL did not respond with usable data.
					return;
				}
				this.fetchedLocationName = data.properties.relativeLocation.properties.city + ", " + data.properties.relativeLocation.properties.state;
				Log.log("Forecast location is " + this.fetchedLocationName);
				this.forecastURL = data.properties.forecast;
				this.forecastHourlyURL = data.properties.forecastHourly;
				this.forecastGridDataURL = data.properties.forecastGridData;
				this.observationStationsURL = data.properties.observationStations;
				// with this URL, we chain another promise for the station obs URL
				return this.fetchData(data.properties.observationStations);
			})
			.then((obsData) => {
				if (!obsData || !obsData.features) {
					// obs station URL did not respond with usable data.
					return;
				}
				this.stationObsURL = obsData.features[0].id + "/observations/latest";
			})
			.catch((err) => {
				Log.error(err);
			})
			.finally(() => {
				// excellent, let's fetch some actual wx data
				this.configURLs = true;
				this.fetchCurrentWeather();
			});
	},

	/*
	 * Generate a WeatherObject based on currentWeatherInformation
	 * Weather.gov API uses specific units; API does not include choice of units
	 * ... object needs data in units based on config!
	 */
	generateWeatherObjectFromCurrentWeather(currentWeatherData) {
		const currentWeather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);

		currentWeather.date = moment(currentWeatherData.timestamp);
		currentWeather.temperature = this.convertTemp(currentWeatherData.temperature.value);
		currentWeather.windSpeed = this.convertSpeed(currentWeatherData.windSpeed.value);
		currentWeather.windDirection = currentWeatherData.windDirection.value;
		currentWeather.minTemperature = this.convertTemp(currentWeatherData.minTemperatureLast24Hours.value);
		currentWeather.maxTemperature = this.convertTemp(currentWeatherData.maxTemperatureLast24Hours.value);
		currentWeather.humidity = Math.round(currentWeatherData.relativeHumidity.value);
		currentWeather.rain = null;
		currentWeather.snow = null;
		currentWeather.precipitation = this.convertLength(currentWeatherData.precipitationLastHour.value);
		currentWeather.feelsLikeTemp = this.convertTemp(currentWeatherData.heatIndex.value);

		let isDaytime = true;
		if (currentWeatherData.icon.includes("day")) {
			isDaytime = true;
		} else {
			isDaytime = false;
		}
		currentWeather.weatherType = this.convertWeatherType(currentWeatherData.textDescription, isDaytime);

		// determine the sunrise/sunset times - not supplied in weather.gov data
		let times = this.calcAstroData(this.config.lat, this.config.lon);
		currentWeather.sunrise = times[0];
		currentWeather.sunset = times[1];

		return currentWeather;
	},

	/*
	 * Generate WeatherObjects based on forecast information
	 */
	generateWeatherObjectsFromForecast(forecasts) {
		return this.fetchForecastDaily(forecasts);
	},

	/*
	 * fetch forecast information for daily forecast.
	 */
	fetchForecastDaily(forecasts) {
		// initial variable declaration
		const days = [];
		// variables for temperature range and rain
		let minTemp = [];
		let maxTemp = [];
		// variable for date
		let date = "";
		let weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);
		weather.precipitation = 0;

		for (const forecast of forecasts) {
			if (date !== moment(forecast.startTime).format("YYYY-MM-DD")) {
				// calculate minimum/maximum temperature, specify rain amount
				weather.minTemperature = Math.min.apply(null, minTemp);
				weather.maxTemperature = Math.max.apply(null, maxTemp);

				// push weather information to days array
				days.push(weather);
				// create new weather-object
				weather = new WeatherObject(this.config.units, this.config.tempUnits, this.config.windUnits, this.config.useKmh);

				minTemp = [];
				maxTemp = [];
				weather.precipitation = 0;

				// set new date
				date = moment(forecast.startTime).format("YYYY-MM-DD");

				// specify date
				weather.date = moment(forecast.startTime);

				// use the forecast isDayTime attribute to help build the weatherType label
				weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
			}

			if (moment(forecast.startTime).format("H") >= 8 && moment(forecast.startTime).format("H") <= 17) {
				weather.weatherType = this.convertWeatherType(forecast.shortForecast, forecast.isDaytime);
			}

			// the same day as before
			// add values from forecast to corresponding variables
			minTemp.push(forecast.temperature);
			maxTemp.push(forecast.temperature);
		}

		// last day
		// calculate minimum/maximum temperature
		weather.minTemperature = Math.min.apply(null, minTemp);
		weather.maxTemperature = Math.max.apply(null, maxTemp);

		// push weather information to days array
		days.push(weather);
		return days.slice(1);
	},

	/*
	 * Unit conversions
	 */
	// conversion to fahrenheit
	convertTemp(temp) {
		if (this.config.tempUnits === "imperial") {
			return (9 / 5) * temp + 32;
		} else {
			return temp;
		}
	},
	// conversion to mph or kmh
	convertSpeed(metSec) {
		if (this.config.windUnits === "imperial") {
			return metSec * 2.23694;
		} else {
			if (this.config.useKmh) {
				return metSec * 3.6;
			} else {
				return metSec;
			}
		}
	},
	// conversion to inches
	convertLength(meters) {
		if (this.config.units === "imperial") {
			return meters * 39.3701;
		} else {
			return meters;
		}
	},

	/*
	 * Calculate the astronomical data
	 */
	calcAstroData(lat, lon) {
		const sunTimes = [];

		// determine the sunrise/sunset times
		let times = SunCalc.getTimes(new Date(), lat, lon);
		sunTimes.push(moment(times.sunrise, "X"));
		sunTimes.push(moment(times.sunset, "X"));

		return sunTimes;
	},

	/*
	 * Convert the icons to a more usable name.
	 */
	convertWeatherType(weatherType, isDaytime) {
		//https://w1.weather.gov/xml/current_obs/weather.php
		// There are way too many types to create, so lets just look for certain strings

		if (weatherType.includes("Cloudy") || weatherType.includes("Partly")) {
			if (isDaytime) {
				return "day-cloudy";
			}

			return "night-cloudy";
		} else if (weatherType.includes("Overcast")) {
			if (isDaytime) {
				return "cloudy";
			}

			return "night-cloudy";
		} else if (weatherType.includes("Freezing") || weatherType.includes("Ice")) {
			return "rain-mix";
		} else if (weatherType.includes("Snow")) {
			if (isDaytime) {
				return "snow";
			}

			return "night-snow";
		} else if (weatherType.includes("Thunderstorm")) {
			if (isDaytime) {
				return "thunderstorm";
			}

			return "night-thunderstorm";
		} else if (weatherType.includes("Showers")) {
			if (isDaytime) {
				return "showers";
			}

			return "night-showers";
		} else if (weatherType.includes("Rain") || weatherType.includes("Drizzle")) {
			if (isDaytime) {
				return "rain";
			}

			return "night-rain";
		} else if (weatherType.includes("Breezy") || weatherType.includes("Windy")) {
			if (isDaytime) {
				return "cloudy-windy";
			}

			return "night-alt-cloudy-windy";
		} else if (weatherType.includes("Fair") || weatherType.includes("Clear") || weatherType.includes("Few") || weatherType.includes("Sunny")) {
			if (isDaytime) {
				return "day-sunny";
			}

			return "night-clear";
		} else if (weatherType.includes("Dust") || weatherType.includes("Sand")) {
			return "dust";
		} else if (weatherType.includes("Fog")) {
			return "fog";
		} else if (weatherType.includes("Smoke")) {
			return "smoke";
		} else if (weatherType.includes("Haze")) {
			return "day-haze";
		}

		return null;
	},

	/*
	Convert the direction into Degrees
	*/
	convertWindDirection(windDirection) {
		const windCardinals = {
			N: 0,
			NNE: 22,
			NE: 45,
			ENE: 67,
			E: 90,
			ESE: 112,
			SE: 135,
			SSE: 157,
			S: 180,
			SSW: 202,
			SW: 225,
			WSW: 247,
			W: 270,
			WNW: 292,
			NW: 315,
			NNW: 337
		};

		return windCardinals.hasOwnProperty(windDirection) ? windCardinals[windDirection] : null;
	}
});
<|endoftext|>


<commit_before><commit_msg>Update sumorobot.js<commit_after><|endoftext|>


<commit_before>jQuery.noConflict();
jQuery(function($j) {
	var COOKIE_NAME = 'dj_debug_panel';
	$j.djDebug = function(data, klass) {
		$j.djDebug.init();
	}
	$j.extend($j.djDebug, {
		init: function() {
			var current = null;
			$j('#djDebugPanelList li a').click(function() {
				if (!this.className) {
					return false;
				}
				current = $j('#djDebug #' + this.className);
				if (current.is(':visible')) {
				    $j(document).trigger('close.djDebug');
					$j(this).parent().removeClass('active');
				} else {
					$j('.panelContent').hide(); // Hide any that are already open
					current.show();
					$j.djDebug.open();
					$j('#djDebugToolbar li').removeClass('active');
					$j(this).parent().addClass('active');
				}
				return false;
			});
			$j('#djDebug a.close').click(function() {
				$j(document).trigger('close.djDebug');
				return false;
			});
			$j('#djDebug a.remoteCall').click(function() {
				$j('#djDebugWindow').load(this.href, {}, function() {
					$j('#djDebugWindow a.back').click(function() {
						$j(this).parent().parent().hide();
						return false;
					});
				});
				$j('#djDebugWindow').show();
				return false;
			});
			$j('#djDebugTemplatePanel a.djTemplateShowContext').click(function() {
				$j.djDebug.toggle_arrow($j(this).children('.toggleArrow'))
				$j.djDebug.toggle_content($j(this).parent().next());
				return false;
			});
			$j('#djDebugSQLPanel a.djSQLShowStacktrace').click(function() {
				$j.djDebug.toggle_content($j(this).parent().next());
				return false;
			});
			$j('#djHideToolBarButton').click(function() {
				$j.djDebug.hide_toolbar(true);
				return false;
			});
			$j('#djShowToolBarButton').click(function() {
				$j.djDebug.show_toolbar();
				return false;
			});
			if ($j.cookie(COOKIE_NAME)) {
				$j.djDebug.hide_toolbar(false);
			} else {
				$j.djDebug.show_toolbar(false);
			}
		},
		open: function() {
			// TODO: Decide if we should remove this
		},
		toggle_content: function(elem) {
			if (elem.is(':visible')) {
				elem.hide();
			} else {
				elem.show();
			}
		},
		close: function() {
			$j(document).trigger('close.djDebug');
			return false;
		},
		hide_toolbar: function(setCookie) {
			// close any sub panels
			$j('#djDebugWindow').hide();
			// close all panels
			$j('.panelContent').hide();
			$j('#djDebugToolbar li').removeClass('active');
			// finally close toolbar
			$j('#djDebugToolbar').hide('fast');
			$j('#djDebugToolbarHandle').show();
			// Unbind keydown
			$j(document).unbind('keydown.djDebug');
			if (setCookie) {
				$j.cookie(COOKIE_NAME, 'hide', {
					path: '/',
					expires: 10
				});
			}
		},
		show_toolbar: function(animate) {
			// Set up keybindings
			$j(document).bind('keydown.djDebug', function(e) {
				if (e.keyCode == 27) {
					$j.djDebug.close();
				}
			});
			$j('#djDebugToolbarHandle').hide();
			if (animate) {
				$j('#djDebugToolbar').show('fast');
			} else {
				$j('#djDebugToolbar').show();
			}
			$j.cookie(COOKIE_NAME, null, {
				path: '/',
				expires: -1
			});
		},
		toggle_arrow: function(elem) {
			var uarr = String.fromCharCode(0x25b6);
			var darr = String.fromCharCode(0x25bc);
			elem.html(elem.html() == uarr ? darr : uarr);
		}
	});
	$j(document).bind('close.djDebug', function() {
		// If a sub-panel is open, close that
		if ($j('#djDebugWindow').is(':visible')) {
			$j('#djDebugWindow').hide();
			return;
		}
		// If a panel is open, close that
		if ($j('.panelContent').is(':visible')) {
			$j('.panelContent').hide();
			return;
		}
		// Otherwise, just minimize the toolbar
		if ($j('#djDebugToolbar').is(':visible')) {
			$j.djDebug.hide_toolbar(true);
			return;
		}
	});
});
jQuery(function() {
	jQuery.djDebug();
});
<commit_msg>Fixes li class="active" when panel closed via close button.<commit_after>jQuery.noConflict();
jQuery(function($j) {
	var COOKIE_NAME = 'dj_debug_panel';
	$j.djDebug = function(data, klass) {
		$j.djDebug.init();
	}
	$j.extend($j.djDebug, {
		init: function() {
			var current = null;
			$j('#djDebugPanelList li a').click(function() {
				if (!this.className) {
					return false;
				}
				current = $j('#djDebug #' + this.className);
				if (current.is(':visible')) {
				    $j(document).trigger('close.djDebug');
					$j(this).parent().removeClass('active');
				} else {
					$j('.panelContent').hide(); // Hide any that are already open
					current.show();
					$j.djDebug.open();
					$j('#djDebugToolbar li').removeClass('active');
					$j(this).parent().addClass('active');
				}
				return false;
			});
			$j('#djDebug a.close').click(function() {
				$j(document).trigger('close.djDebug');
				$j('#djDebugToolbar li').removeClass('active');
				return false;
			});
			$j('#djDebug a.remoteCall').click(function() {
				$j('#djDebugWindow').load(this.href, {}, function() {
					$j('#djDebugWindow a.back').click(function() {
						$j(this).parent().parent().hide();
						return false;
					});
				});
				$j('#djDebugWindow').show();
				return false;
			});
			$j('#djDebugTemplatePanel a.djTemplateShowContext').click(function() {
				$j.djDebug.toggle_arrow($j(this).children('.toggleArrow'))
				$j.djDebug.toggle_content($j(this).parent().next());
				return false;
			});
			$j('#djDebugSQLPanel a.djSQLShowStacktrace').click(function() {
				$j.djDebug.toggle_content($j(this).parent().next());
				return false;
			});
			$j('#djHideToolBarButton').click(function() {
				$j.djDebug.hide_toolbar(true);
				return false;
			});
			$j('#djShowToolBarButton').click(function() {
				$j.djDebug.show_toolbar();
				return false;
			});
			if ($j.cookie(COOKIE_NAME)) {
				$j.djDebug.hide_toolbar(false);
			} else {
				$j.djDebug.show_toolbar(false);
			}
		},
		open: function() {
			// TODO: Decide if we should remove this
		},
		toggle_content: function(elem) {
			if (elem.is(':visible')) {
				elem.hide();
			} else {
				elem.show();
			}
		},
		close: function() {
			$j(document).trigger('close.djDebug');
			return false;
		},
		hide_toolbar: function(setCookie) {
			// close any sub panels
			$j('#djDebugWindow').hide();
			// close all panels
			$j('.panelContent').hide();
			$j('#djDebugToolbar li').removeClass('active');
			// finally close toolbar
			$j('#djDebugToolbar').hide('fast');
			$j('#djDebugToolbarHandle').show();
			// Unbind keydown
			$j(document).unbind('keydown.djDebug');
			if (setCookie) {
				$j.cookie(COOKIE_NAME, 'hide', {
					path: '/',
					expires: 10
				});
			}
		},
		show_toolbar: function(animate) {
			// Set up keybindings
			$j(document).bind('keydown.djDebug', function(e) {
				if (e.keyCode == 27) {
					$j.djDebug.close();
				}
			});
			$j('#djDebugToolbarHandle').hide();
			if (animate) {
				$j('#djDebugToolbar').show('fast');
			} else {
				$j('#djDebugToolbar').show();
			}
			$j.cookie(COOKIE_NAME, null, {
				path: '/',
				expires: -1
			});
		},
		toggle_arrow: function(elem) {
			var uarr = String.fromCharCode(0x25b6);
			var darr = String.fromCharCode(0x25bc);
			elem.html(elem.html() == uarr ? darr : uarr);
		}
	});
	$j(document).bind('close.djDebug', function() {
		// If a sub-panel is open, close that
		if ($j('#djDebugWindow').is(':visible')) {
			$j('#djDebugWindow').hide();
			return;
		}
		// If a panel is open, close that
		if ($j('.panelContent').is(':visible')) {
			$j('.panelContent').hide();
			return;
		}
		// Otherwise, just minimize the toolbar
		if ($j('#djDebugToolbar').is(':visible')) {
			$j.djDebug.hide_toolbar(true);
			return;
		}
	});
});
jQuery(function() {
	jQuery.djDebug();
});
<|endoftext|>


<commit_before>import { FILTERS_IMPORT } from '../actions/importer';
import { FILTERS_FETCH_SUCCESS, FILTERS_CREATE_SUCCESS } from '../actions/filters';
import { Map as ImmutableMap, is, fromJS } from 'immutable';

const normalizeFilter = (state, filter) => {
  const normalizedFilter = fromJS({
    id: filter.id,
    title: filter.title,
    context: filter.context,
    filter_action: filter.filter_action,
    keywords: filter.keywords,
    expires_at: filter.expires_at ? Date.parse(filter.expires_at) : null,
  });

  if (is(state.get(filter.id), normalizedFilter)) {
    return state;
  } else {
    // Do not overwrite keywords when receiving a partial filter
    return state.update(filter.id, ImmutableMap(), (old) => (
      old.mergeWith(((old_value, new_value) => (new_value === undefined ? old_value : new_value)), normalizedFilter)
    ));
  }
};

const normalizeFilters = (state, filters) => {
  filters.forEach(filter => {
    state = normalizeFilter(state, filter);
  });

  return state;
};

export default function filters(state = ImmutableMap(), action) {
  switch(action.type) {
  case FILTERS_CREATE_SUCCESS:
    return normalizeFilter(state, action.filter);
  case FILTERS_FETCH_SUCCESS:
    //TODO: handle deleting obsolete filters
  case FILTERS_IMPORT:
    return normalizeFilters(state, action.filters);
  default:
    return state;
  }
};
<commit_msg>Fix “Filter this post” potentially listing deleted filters (#19051)<commit_after>import { FILTERS_IMPORT } from '../actions/importer';
import { FILTERS_FETCH_SUCCESS, FILTERS_CREATE_SUCCESS } from '../actions/filters';
import { Map as ImmutableMap, is, fromJS } from 'immutable';

const normalizeFilter = (state, filter) => {
  const normalizedFilter = fromJS({
    id: filter.id,
    title: filter.title,
    context: filter.context,
    filter_action: filter.filter_action,
    keywords: filter.keywords,
    expires_at: filter.expires_at ? Date.parse(filter.expires_at) : null,
  });

  if (is(state.get(filter.id), normalizedFilter)) {
    return state;
  } else {
    // Do not overwrite keywords when receiving a partial filter
    return state.update(filter.id, ImmutableMap(), (old) => (
      old.mergeWith(((old_value, new_value) => (new_value === undefined ? old_value : new_value)), normalizedFilter)
    ));
  }
};

const normalizeFilters = (state, filters) => {
  filters.forEach(filter => {
    state = normalizeFilter(state, filter);
  });

  return state;
};

export default function filters(state = ImmutableMap(), action) {
  switch(action.type) {
  case FILTERS_CREATE_SUCCESS:
    return normalizeFilter(state, action.filter);
  case FILTERS_FETCH_SUCCESS:
    return normalizeFilters(ImmutableMap(), action.filters);
  case FILTERS_IMPORT:
    return normalizeFilters(state, action.filters);
  default:
    return state;
  }
};
<|endoftext|>


<commit_before>/**
 * BMotionWeb for ProB Execute Event Handler
 *
 */
define([
  'angular',
  'jquery',
  'bms.func',
  'bms.modal',
  'bms.ws',
  'prob.ws',
  'bms.session',
  'bms.visualization',
  'qtip'
], function(angular, $, bms) {

  return angular.module('prob.handlers.event', ['bms.modal', 'bms.ws', 'prob.ws', 'bms.session', 'bms.visualization'])
    .factory('executeEventEvent', ['ws', '$q', 'bmsModalService', 'bmsVisualizationService', 'bmsWsService', 'probWsService',
      function(ws, $q, bmsModalService, bmsVisualizationService, bmsWsService, probWsService) {
        'use strict';

        var event = function(view, options) {
          this.id = bms.uuid();
          this.view = view;
          this.options = this.getDefaultOptions(options);
        };

        event.prototype.getDefaultOptions = function(options) {

          var self = this;

          var fOptions = angular.merge({
            events: [],
            label: function(origin, event, container) {
              var predicateStr = event.predicate ? '(' + event.predicate + ')' : '';
              return '' + event.name + predicateStr + '';
            },
            callback: function() {}
          }, options);

          if (options.name) {
            fOptions.events.push({
              name: options.name,
              predicate: options.predicate
            });
          }

          // TODO This is a workaround since angular.merge destroys the actual
          // element after merging - consequently we manually add this element
          fOptions.element = options.element;

          return fOptions;

        };

        event.prototype.getId = function() {
          return this.id;
        };

        event.prototype.shouldBeChecked = function() {
          var self = this;
          var session = self.view.session;
          if (session.isBVisualization()) {
            if (session.toolData.model !== undefined) {
              var refinements = session.toolData.model.refinements;
              if (refinements) {
                return self.options.refinement ? bms.inArray(self.options.refinement, refinements) : true;
              }
            }
          }
          return true;
        };

        event.prototype.getTooltipContent = function(sessionId, element, container, traceId, api) {

          var defer = $q.defer();

          var self = this;

          probWsService.checkEvents(
              sessionId,
              traceId,
              bms.normalize(self.options.events, [], element, container)
            )
            .then(function(events) {

              // Build tooltip content
              var tt_container = $('

');
              var tt_ul = $('

');
              angular.forEach(events, function(evt) {

                var iconSpan = $('')
                  .css("margin-right", "2px")
                  .addClass('glyphicon')
                  .addClass(evt.canExecute ? 'glyphicon-ok-circle' : 'glyphicon-remove-circle');

                var labelSpan = $('' + bms.convertFunction('origin,event,container', self.options.label)(element, evt, container) + '');
                if (evt.canExecute) {
                  var callbackFunc = bms.convertFunction('origin,data,container', self.options.callback);
                  labelSpan.click(function() {
                    bmsWsService.executeEvent(sessionId, {
                      traceId: traceId,
                      name: evt.name,
                      predicate: evt.predicate,
                      executor: element.attr("id") ? element.attr("id") : 'unknown'
                    }).then(function(result) {
                      callbackFunc(element, result, container);
                    }, function(err) {
                      bmsModalService.openErrorDialog(err);
                    }).finally(function() {
                      api.hide();
                    });
                  });
                }

                tt_ul.append($('

')
                  .addClass(evt.canExecute ? 'enabled' : 'disabled')
                  .addClass(evt.canExecute ? 'cursor-pointer' : 'cursor-default')
                  .append(iconSpan)
                  .append(labelSpan));

              });

              tt_container.append(tt_ul);

              defer.resolve(tt_container);

            }, function(err) {
              bmsModalService.openErrorDialog(err);
            });

          return defer.promise;

        };

        event.prototype.initTooltip = function(sessionId, element, container, traceId) {

          var self = this;

          return element.qtip({
            content: {
              text: function(event, api) {
                return self.getTooltipContent(sessionId, element, container, traceId, api)
                  .then(function(tooltipElement) {
                    return tooltipElement;
                  }, function(err) {
                    bms.openErrorDialog(err);
                  });
              }
            },
            position: {
              my: 'bottom left',
              at: 'top right',
              effect: false,
              viewport: container
                /*,
                adjust: {
                  x: container.offset().left,
                  y: container.offset().top
                }*/
            },
            events: {
              show: function(event, api) {
                var qtipDisable = element.data('qtip-disable') ? element.data('qtip-disable') : false;
                if (event['originalEvent']['type'] === "mouseover" && qtipDisable) {
                  event.preventDefault();
                }
              }
            },
            show: {
              delay: 1200,
              event: 'mouseover'
            },
            hide: {
              fixed: true,
              delay: 400
            },
            style: {
              classes: 'qtip-light qtip-bootstrap'
            }
          });

        };

        event.prototype.setup = function() {

          var defer = $q.defer();

          var self = this;

          if (!self.options.selector && !self.options.element) {
            defer.reject("Please specify a selector or an element for execute event handler.");
          } else {

            var traceId = self.view.toolOptions.traceId;
            var jcontainer = self.view.container.contents();

            var jelements = self.options.element ? $(self.options.element) : self.view.container.contents().find(self.options.selector);

            jelements.each(function(i, ele) {

              var jele = $(ele);
              jele.css('cursor', 'pointer');

              var tooltip = self.initTooltip(self.view.session.id, jele, jcontainer, traceId);
              var api = tooltip.qtip('api');
              var callbackFunc = bms.convertFunction('origin,data,container', self.options.callback);

              jele.click(function(event) {

                // Get more information about event (e.g. enabled/disabled)
                probWsService.checkEvents(
                    self.view.session.id,
                    traceId,
                    bms.normalize(self.options.events, [], jele, jcontainer)
                  )
                  .then(function(events) {

                    // Filter enabled events
                    var enabledEvents = events.filter(function(evt) {
                      return evt.canExecute;
                    });

                    // If only one events is enabled of the list of events, execute it ...
                    if (enabledEvents.length === 1) {

                      var name = enabledEvents[0].name; // Event name
                      var predicate = enabledEvents[0].predicate; // Event predicate

                      // Execute event with name and predicate
                      bmsWsService.executeEvent(self.view.session.id, {
                        traceId: traceId,
                        name: name,
                        predicate: predicate,
                        executor: jele.attr("id") ? jele.attr("id") : 'unknown'
                      }).then(function(data) {
                        // Data includes return values from classicalB operation (returnValues)
                        // and the new state id (stateId).
                        // Additionally set event name and predicate in callback data
                        data.name = name;
                        data.predicate = predicate;
                        // Call callback function with data
                        callbackFunc(jele, data, jcontainer);
                      }, function(err) {
                        // If an error occurred while executing the event,
                        // open error dialog with error message
                        bmsModalService.openErrorDialog(err);
                      }).finally(function() {
                        // Finally hide tooltip
                        api.hide();
                      });

                    } else {
                      // ... else show a popup displaying the available events
                      api.show('click');
                    }

                    jele.data('qtip-disable', true);

                  }, function(err) {
                    // If an error occurred while receiving
                    // additional information about events,
                    // open error dialog with error message
                    bmsModalService.openErrorDialog(err);
                  });

              }).mouseout(function() {
                jele.data('qtip-disable', false);
              });

            });

          }

          defer.resolve();

          return defer.promise;

        };

        return event;

      }
    ]);

});
<commit_msg>increased max width of event handler tooltip<commit_after>/**
 * BMotionWeb for ProB Execute Event Handler
 *
 */
define([
  'angular',
  'jquery',
  'bms.func',
  'bms.modal',
  'bms.ws',
  'prob.ws',
  'bms.session',
  'bms.visualization',
  'qtip'
], function(angular, $, bms) {

  return angular.module('prob.handlers.event', ['bms.modal', 'bms.ws', 'prob.ws', 'bms.session', 'bms.visualization'])
    .factory('executeEventEvent', ['ws', '$q', 'bmsModalService', 'bmsVisualizationService', 'bmsWsService', 'probWsService',
      function(ws, $q, bmsModalService, bmsVisualizationService, bmsWsService, probWsService) {
        'use strict';

        var event = function(view, options) {
          this.id = bms.uuid();
          this.view = view;
          this.options = this.getDefaultOptions(options);
        };

        event.prototype.getDefaultOptions = function(options) {

          var self = this;

          var fOptions = angular.merge({
            events: [],
            label: function(origin, event, container) {
              var predicateStr = event.predicate ? '(' + event.predicate + ')' : '';
              return '' + event.name + predicateStr + '';
            },
            callback: function() {}
          }, options);

          if (options.name) {
            fOptions.events.push({
              name: options.name,
              predicate: options.predicate
            });
          }

          // TODO This is a workaround since angular.merge destroys the actual
          // element after merging - consequently we manually add this element
          fOptions.element = options.element;

          return fOptions;

        };

        event.prototype.getId = function() {
          return this.id;
        };

        event.prototype.shouldBeChecked = function() {
          var self = this;
          var session = self.view.session;
          if (session.isBVisualization()) {
            if (session.toolData.model !== undefined) {
              var refinements = session.toolData.model.refinements;
              if (refinements) {
                return self.options.refinement ? bms.inArray(self.options.refinement, refinements) : true;
              }
            }
          }
          return true;
        };

        event.prototype.getTooltipContent = function(sessionId, element, container, traceId, api) {

          var defer = $q.defer();

          var self = this;

          probWsService.checkEvents(
              sessionId,
              traceId,
              bms.normalize(self.options.events, [], element, container)
            )
            .then(function(events) {

              // Build tooltip content
              var tt_container = $('

');
              var tt_ul = $('

');
              angular.forEach(events, function(evt) {

                var iconSpan = $('')
                  .css("margin-right", "2px")
                  .addClass('glyphicon')
                  .addClass(evt.canExecute ? 'glyphicon-ok-circle' : 'glyphicon-remove-circle');

                var labelSpan = $('' + bms.convertFunction('origin,event,container', self.options.label)(element, evt, container) + '');
                if (evt.canExecute) {
                  var callbackFunc = bms.convertFunction('origin,data,container', self.options.callback);
                  labelSpan.click(function() {
                    bmsWsService.executeEvent(sessionId, {
                      traceId: traceId,
                      name: evt.name,
                      predicate: evt.predicate,
                      executor: element.attr("id") ? element.attr("id") : 'unknown'
                    }).then(function(result) {
                      callbackFunc(element, result, container);
                    }, function(err) {
                      bmsModalService.openErrorDialog(err);
                    }).finally(function() {
                      api.hide();
                    });
                  });
                }

                tt_ul.append($('

')
                  .addClass(evt.canExecute ? 'enabled' : 'disabled')
                  .addClass(evt.canExecute ? 'cursor-pointer' : 'cursor-default')
                  .append(iconSpan)
                  .append(labelSpan));

              });

              tt_container.append(tt_ul);

              defer.resolve(tt_container);

            }, function(err) {
              bmsModalService.openErrorDialog(err);
            });

          return defer.promise;

        };

        event.prototype.initTooltip = function(sessionId, element, container, traceId) {

          var self = this;

          return element.qtip({
            content: {
              text: function(event, api) {
                return self.getTooltipContent(sessionId, element, container, traceId, api)
                  .then(function(tooltipElement) {
                    return tooltipElement;
                  }, function(err) {
                    bms.openErrorDialog(err);
                  });
              }
            },
            position: {
              my: 'bottom left',
              at: 'top right',
              effect: false,
              viewport: container
                /*,
                adjust: {
                  x: container.offset().left,
                  y: container.offset().top
                }*/
            },
            events: {
              show: function(event, api) {
                var qtipDisable = element.data('qtip-disable') ? element.data('qtip-disable') : false;
                if (event['originalEvent']['type'] === "mouseover" && qtipDisable) {
                  event.preventDefault();
                }
              }
            },
            show: {
              delay: 1200,
              event: 'mouseover'
            },
            hide: {
              fixed: true,
              delay: 400
            },
            style: {
              classes: 'qtip-light qtip-bootstrap'
            }
          });

        };

        event.prototype.setup = function() {

          var defer = $q.defer();

          var self = this;

          if (!self.options.selector && !self.options.element) {
            defer.reject("Please specify a selector or an element for execute event handler.");
          } else {

            var traceId = self.view.toolOptions.traceId;
            var jcontainer = self.view.container.contents();

            var jelements = self.options.element ? $(self.options.element) : self.view.container.contents().find(self.options.selector);

            jelements.each(function(i, ele) {

              var jele = $(ele);
              jele.css('cursor', 'pointer');

              var tooltip = self.initTooltip(self.view.session.id, jele, jcontainer, traceId);
              var api = tooltip.qtip('api');
              var callbackFunc = bms.convertFunction('origin,data,container', self.options.callback);

              jele.click(function(event) {

                // Get more information about event (e.g. enabled/disabled)
                probWsService.checkEvents(
                    self.view.session.id,
                    traceId,
                    bms.normalize(self.options.events, [], jele, jcontainer)
                  )
                  .then(function(events) {

                    // Filter enabled events
                    var enabledEvents = events.filter(function(evt) {
                      return evt.canExecute;
                    });

                    // If only one events is enabled of the list of events, execute it ...
                    if (enabledEvents.length === 1) {

                      var name = enabledEvents[0].name; // Event name
                      var predicate = enabledEvents[0].predicate; // Event predicate

                      // Execute event with name and predicate
                      bmsWsService.executeEvent(self.view.session.id, {
                        traceId: traceId,
                        name: name,
                        predicate: predicate,
                        executor: jele.attr("id") ? jele.attr("id") : 'unknown'
                      }).then(function(data) {
                        // Data includes return values from classicalB operation (returnValues)
                        // and the new state id (stateId).
                        // Additionally set event name and predicate in callback data
                        data.name = name;
                        data.predicate = predicate;
                        // Call callback function with data
                        callbackFunc(jele, data, jcontainer);
                      }, function(err) {
                        // If an error occurred while executing the event,
                        // open error dialog with error message
                        bmsModalService.openErrorDialog(err);
                      }).finally(function() {
                        // Finally hide tooltip
                        api.hide();
                      });

                    } else {
                      // ... else show a popup displaying the available events
                      api.show('click');
                    }

                    jele.data('qtip-disable', true);

                  }, function(err) {
                    // If an error occurred while receiving
                    // additional information about events,
                    // open error dialog with error message
                    bmsModalService.openErrorDialog(err);
                  });

              }).mouseout(function() {
                jele.data('qtip-disable', false);
              });

            });

          }

          defer.resolve();

          return defer.promise;

        };

        return event;

      }
    ]);

});
<|endoftext|>