<commit_before>define([
  'text!templates/SLDlegend.html',
  'i18n!localization/nls/SLDeditor',
  'lodash',
  'backbone',
  'jquery',
  'svg',
  'validation/validate'

], function(SLDLegendTemplate, locale, _, Backbone, $, SVG, validateFunctions) {
  var SLDLegendView = Backbone.View.extend({
  	className: 'SLDlegend',
    template: _.template(SLDLegendTemplate),
    events: {
            'click .closeLegend': 'closeLegend',
    },

    initialize: function(params) {
	    this.dispatcher = params.dispatcher;
	    this.listenTo(this.dispatcher, "renderLegend", this.renderLegend);
	    this.listenTo(this.dispatcher, "renderPreview", this.renderPreview);
	    this.listenTo(this.dispatcher, "setParam", this.setParam);
	    this.listenTo(this.dispatcher, "setSymbolType", this.setSymbolType);
	    this.listenTo(this.dispatcher, "symbolizerChanged", this.symbolizerChanged);
	    this.listenTo(this.dispatcher, "setSymbolizerId", this.setSymbolizerId);
	    this.listenTo(this.dispatcher, "all", this.logger);
	    this.configs = params.configs;
	    this.templates = params.templates;
	},

	renderLegend: function(event) {
		event.preventDefault();
		var self = this;
        var configId = $(event.currentTarget).data('id');
        var configModel = self.configs.getById(configId);
        var templateModel = self.templates.getById(configModel.get('template_id'));
        var SLDfeaturetypeTree = templateModel.getFeaturetypeTree();
        self.$el.html(self.template({SLDfeaturetypeTree: SLDfeaturetypeTree, locale: locale, configName: configModel.attributes.name}));

        var symbolizers = templateModel.get('sld_symbolizers');

        _.forEach(symbolizers, function (symbolizer) {
        	var params = templateModel.getParamsBySymbolizerId(symbolizer.id);
        	var paramlist = configModel.getSLDValuesByParams(params);
        	var symbolizerType = symbolizer.type.toLowerCase();
        	var previewElementId = "symbolizer-preview-" + symbolizer.id;
        	self.renderPreview(paramlist, symbolizerType, previewElementId, "small");
        });
	},

	closeLegend: function () {
	    $(this.el).empty();
	},

	symbolizerChanged: function (activeSymbolizer) {
		this.activeSymbolizer = activeSymbolizer;
	},

	renderPreview: function (params, symbolType, previewElementId, previewSize) {
	    this.attributes = {};
	    this.preview = SVG(previewElementId);
	    if (symbolType === "pointsymbolizer") {
	      this.renderPoint(params, previewSize);
	    } else if (symbolType === "linesymbolizer") {
	      this.renderLine(params, previewSize);
	    } else if (symbolType === "polygonsymbolizer") {
	      this.renderPolygon(params, previewSize);
	    } else if (symbolType === "textsymbolizer") {
	      this.renderText(params, previewSize);
	    } else {
	      console.log("geometryType of params is not defined");
	    }
	},

	renderPoint: function (params, previewSize) {
	    // graphic paths are from this website: http://raphaeljs.com/icons/
	    this.graphicPaths = {
	      "triangle": "M23.963,20.834L17.5,9.64c-0.825-1.429-2.175-1.429-3,0L8.037,20.834c-0.825,1.429-0.15,2.598,1.5,2.598h12.926C24.113,23.432,24.788,22.263,23.963,20.834z",
	      "star": "M16,22.375L7.116,28.83l3.396-10.438l-8.883-6.458l10.979,0.002L16.002,1.5l3.391,10.434h10.981l-8.886,6.457l3.396,10.439L16,22.375L16,22.375z",
	      "cross": "M25.979,12.896 19.312,12.896 19.312,6.229 12.647,6.229 12.647,12.896 5.979,12.896 5.979,19.562 12.647,19.562 12.647,26.229 19.312,26.229 19.312,19.562 25.979,19.562z",
	      "x": "M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z",
	      "square": "M5.5,5.5h20v20h-20z"
	    };

	    //parse attributes and check if the element is Mark or ExternalGraphic
	    var hasWellKnownName = false;
	    for (var i=0; i < params.length; i++) {
	      if (params[i].attributeName === "wellknownname") {
	        hasWellKnownName = true;
	        var wellknownname = params[i].value
	      } else if (params[i].attributeName === "size") {
	        this.elementSize = params[i].value;
	      } else if (params[i].attributeName === "rotation") {
	        this.elementRotation = params[i].value;
	      } else {
	        var attribute = params[i].attributeName;
	        var attributeValue = params[i].value;
	        this.attributes[attribute] = attributeValue;
	      }
	    }

	    if (previewSize	=== "small") {
	    	this.elementSize = 20;
	    } else {
	    	this.elementSize = 50;
	    }
	    this.elementRotation = 0;
	    if (_.has(this.attributes, "stroke")) {
	      this.attributes["stroke-width"] = 3;
	    }
	    //create preview element
	    if (hasWellKnownName === true) {
	      this.renderWellKnownName(wellknownname);
	    } else {
	      this.renderExternalGraphics();
	    }
	},

	renderWellKnownName: function(wellknownname) {
	    this.preview.clear();
	    if (!_.has(this.graphicPaths, wellknownname)) {

	      this.previewElement = this.preview.circle(this.elementSize);
	    } else {
	      var path = this.graphicPaths[wellknownname];
	      this.previewElement = this.preview.path(path);
	      this.previewElement.size(this.elementSize);
	    }
	    this.previewElement.attr(this.attributes);
	    this.previewElement.transform({rotation: this.elementRotation});
	},

	renderExternalGraphics: function () {
	    console.log('External graphics are not yet supported');
	    //this.previewElement = this.preview.image(this.xlink, this.elementSize);
	    //this.previewElement.center(75, 75);
	    //this.previewElement.transform({rotation: this.elementRotation});
	},

	renderLine: function (params, previewSize) {
	    this.preview.clear();
	    for (var i=0; i < params.length; i++) {
	      var attribute = params[i].attributeName;
	      var attributeValue = params[i].value;
	      if (attribute !== "stroke-width") {
	        this.attributes[attribute] = attributeValue;
	      }
	    }
	    if (previewSize	=== "small") {
	    	this.previewElement = this.preview.line(10, 10, 25, 25).stroke({width: 4});
	    } else {
	    	this.previewElement = this.preview.line(10, 10, 60, 60).stroke({width: 8});
	    }
	    this.previewElement.attr(this.attributes);
	},

	renderPolygon: function (params, previewSize) {
	    this.preview.clear();
	    var strokeWidth = false;
	    for (var i=0; i < params.length; i++) {
	      var attribute = params[i].attributeName;
	      var attributeValue = params[i].value;
	      if (attribute === "stroke-width") {
	        strokeWidth = true
	      } else {
	        this.attributes[attribute] = attributeValue;
	      }
	    }

	    if (previewSize	=== "small") {
	    	var elementWidth = 25;
	    	var elementHeight = 25;
	    } else {
	    	var elementWidth = 60;
	    	var elementHeight = 60;
	    }
	    this.previewElement = this.preview.rect(elementWidth,elementHeight).fill('none');
	    this.previewElement.attr(this.attributes);
	    if (strokeWidth === true) {
	      if (previewSize === "small") {
	        this.previewElement.attr({"stroke-width": 1});
	      } else {
	        this.previewElement.attr({"stroke-width": 4});
	      }
	    }
	},

	renderText: function (params, previewSize) {
	    this.preview.clear();
	    for (i=0; i < params.length; i++) {
	      var attribute = params[i].attributeName;
	      var attributeValue = params[i].value;
	      if (attribute === "font-family" || attribute === "font-style" || attribute === "font-weight" || attribute === "fill" || attribute === "halo-color" || attribute === "halo-radius") {
	        this.attributes[attribute] = attributeValue;
	      }
	    }

	    if (previewSize === "small") {
	    	this.previewElement = this.preview.text("Text!").font({size: 15});
	    } else {
	    	this.previewElement = this.preview.text("Text!").font({size: 30});
	   	}
	    this.previewElement.attr(this.attributes);
	},

	updatePreview: function () {
	    if (_.has(this.attributes, "stroke") && (this.symbolType === "pointsymbolizer" || this.symbolType === "polygonsymbolizer")) {
	      this.attributes["stroke-width"] = 3;
	    } else if (_.has(this.attributes, "size")) {
	    	this.attributes["size"] = 50;
	    }
	    this.previewElement.attr(this.attributes);
	},

	setSymbolType: function (symbolType) {
		this.symbolType = symbolType;
	},

	setSymbolizerId: function (id) {
		this.symbolizerId = id;
	},

	setParam: function(event) {
      var element,
          newvalue,
          param_id,
          param_css_parameter,
          validation;

      var param_id = null;
      var param_css_parameter = null;

      if (event.target.id === "graphic-symbol") {
        element = $(event.target)[0];//.find("#graphic-symbol");
        newvalue = element.value.toLowerCase();

        //if user leaves url invalid and changes the symbol we want to clear url
        if ($("#external-graphic").hasClass("invalid")) {
          var externalGraphicElement = $("#external-graphic")[0];
          externalGraphicElement.value = "";
          validateFunctions.handleValidParam(externalGraphicElement);
        }
        param_id = "" + element.dataset['paramId'];
        param_name = "graphic-symbol";
        validation = validateFunctions.validateParam(element, param_name, newvalue);
        if (validation[0] === "invalid") {
          validateFunctions.handleInvalidParam(validation, element);
          return;
        } else {
          if ($(element).hasClass("invalid")) {
            validateFunctions.handleValidParam(element);
          }
          this.renderWellKnownName(newvalue);
          this.dispatcher.trigger("updateMapStyleByParam",[{'name':'wellknownname','value': newvalue}],this.symbolType, this.symbolizerId);
        }
      } else {
        element = $(event.currentTarget).find(".form-control")[0];
        param_id = "" + element.dataset['paramId'];
        param_css_parameter = element.dataset['cssParameter'];
        newvalue = element.value;
        validation = validateFunctions.validateParam(element, param_css_parameter, newvalue);
        if (validation[0] === "invalid") {
          validateFunctions.handleInvalidParam(validation, element);
          return;
        }
        if ($(element).hasClass("invalid")) {
          validateFunctions.handleValidParam(element);
        }
        if (param_css_parameter === "rotation") {
          this.elementRotation = newvalue;
          this.previewElement.transform({rotation: this.elementRotation});
        // we don't want preview to update size
        } else if (param_css_parameter === "size") {
          this.elementSize = parseInt(newvalue);
        // we don't want preview to update stroke-width
        } else if (param_css_parameter === "stroke-width") {
          //in case we need this later
          this.strokeWidth = parseInt(newvalue);
        } else if (param_css_parameter === "font-size") {
          //in case we need this later
          this.textSize = parseInt(newvalue);
        } else if (param_css_parameter === "stroke-dasharray-part") {
          newvalue = jQuery('input#stroke-dasharray-length').val()+' '+jQuery('input#stroke-dasharray-space').val();
          this.attributes["stroke-dasharray"] = newvalue;
          this.updatePreview();
        } else {
          this.attributes[param_css_parameter] = newvalue;
          this.updatePreview();
        }
        // Update map style
        this.dispatcher.trigger("updateMapStyleByParam",[{'name':param_css_parameter,'value': newvalue}], this.activeSymbolizer.type, this.activeSymbolizer.id );
      }

      this.dispatcher.trigger("updateModelParam", param_id, newvalue);
    }
	});

  	return SLDLegendView;

});<commit_msg>don't update element size in symbolizer preview<commit_after>define([
  'text!templates/SLDlegend.html',
  'i18n!localization/nls/SLDeditor',
  'lodash',
  'backbone',
  'jquery',
  'svg',
  'validation/validate'

], function(SLDLegendTemplate, locale, _, Backbone, $, SVG, validateFunctions) {
  var SLDLegendView = Backbone.View.extend({
  	className: 'SLDlegend',
    template: _.template(SLDLegendTemplate),
    events: {
            'click .closeLegend': 'closeLegend',
    },

    initialize: function(params) {
	    this.dispatcher = params.dispatcher;
	    this.listenTo(this.dispatcher, "renderLegend", this.renderLegend);
	    this.listenTo(this.dispatcher, "renderPreview", this.renderPreview);
	    this.listenTo(this.dispatcher, "setParam", this.setParam);
	    this.listenTo(this.dispatcher, "setSymbolType", this.setSymbolType);
	    this.listenTo(this.dispatcher, "symbolizerChanged", this.symbolizerChanged);
	    this.listenTo(this.dispatcher, "setSymbolizerId", this.setSymbolizerId);
	    this.listenTo(this.dispatcher, "all", this.logger);
	    this.configs = params.configs;
	    this.templates = params.templates;
	},

	renderLegend: function(event) {
		event.preventDefault();
		var self = this;
        var configId = $(event.currentTarget).data('id');
        var configModel = self.configs.getById(configId);
        var templateModel = self.templates.getById(configModel.get('template_id'));
        var SLDfeaturetypeTree = templateModel.getFeaturetypeTree();
        self.$el.html(self.template({SLDfeaturetypeTree: SLDfeaturetypeTree, locale: locale, configName: configModel.attributes.name}));

        var symbolizers = templateModel.get('sld_symbolizers');

        _.forEach(symbolizers, function (symbolizer) {
        	var params = templateModel.getParamsBySymbolizerId(symbolizer.id);
        	var paramlist = configModel.getSLDValuesByParams(params);
        	var symbolizerType = symbolizer.type.toLowerCase();
        	var previewElementId = "symbolizer-preview-" + symbolizer.id;
        	self.renderPreview(paramlist, symbolizerType, previewElementId, "small");
        });
	},

	closeLegend: function () {
	    $(this.el).empty();
	},

	symbolizerChanged: function (activeSymbolizer) {
		this.activeSymbolizer = activeSymbolizer;
	},

	renderPreview: function (params, symbolType, previewElementId, previewSize) {
	    this.attributes = {};
	    this.preview = SVG(previewElementId);
	    if (symbolType === "pointsymbolizer") {
	      this.renderPoint(params, previewSize);
	    } else if (symbolType === "linesymbolizer") {
	      this.renderLine(params, previewSize);
	    } else if (symbolType === "polygonsymbolizer") {
	      this.renderPolygon(params, previewSize);
	    } else if (symbolType === "textsymbolizer") {
	      this.renderText(params, previewSize);
	    } else {
	      console.log("geometryType of params is not defined");
	    }
	},

	renderPoint: function (params, previewSize) {
	    // graphic paths are from this website: http://raphaeljs.com/icons/
	    this.graphicPaths = {
	      "triangle": "M23.963,20.834L17.5,9.64c-0.825-1.429-2.175-1.429-3,0L8.037,20.834c-0.825,1.429-0.15,2.598,1.5,2.598h12.926C24.113,23.432,24.788,22.263,23.963,20.834z",
	      "star": "M16,22.375L7.116,28.83l3.396-10.438l-8.883-6.458l10.979,0.002L16.002,1.5l3.391,10.434h10.981l-8.886,6.457l3.396,10.439L16,22.375L16,22.375z",
	      "cross": "M25.979,12.896 19.312,12.896 19.312,6.229 12.647,6.229 12.647,12.896 5.979,12.896 5.979,19.562 12.647,19.562 12.647,26.229 19.312,26.229 19.312,19.562 25.979,19.562z",
	      "x": "M24.778,21.419 19.276,15.917 24.777,10.415 21.949,7.585 16.447,13.087 10.945,7.585 8.117,10.415 13.618,15.917 8.116,21.419 10.946,24.248 16.447,18.746 21.948,24.248z",
	      "square": "M5.5,5.5h20v20h-20z"
	    };

	    //parse attributes and check if the element is Mark or ExternalGraphic
	    var hasWellKnownName = false;
	    for (var i=0; i < params.length; i++) {
	      if (params[i].attributeName === "wellknownname") {
	        hasWellKnownName = true;
	        var wellknownname = params[i].value
	      } else if (params[i].attributeName === "rotation") {
	        this.elementRotation = params[i].value;
	      } else if (params[i].attributeName !== "size"){
	        var attribute = params[i].attributeName;
	        var attributeValue = params[i].value;
	        this.attributes[attribute] = attributeValue;
	      }
	    }

	    if (previewSize	=== "small") {
	    	this.elementSize = 20;
	    } else {
	    	this.elementSize = 50;
	    }
	    this.elementRotation = 0;
	    if (_.has(this.attributes, "stroke")) {
	      this.attributes["stroke-width"] = 3;
	    }
	    //create preview element
	    if (hasWellKnownName === true) {
	      this.renderWellKnownName(wellknownname);
	    } else {
	      this.renderExternalGraphics();
	    }
	},

	renderWellKnownName: function(wellknownname) {
	    this.preview.clear();
	    if (!_.has(this.graphicPaths, wellknownname)) {

	      this.previewElement = this.preview.circle(this.elementSize);
	    } else {
	      var path = this.graphicPaths[wellknownname];
	      this.previewElement = this.preview.path(path);
	      this.previewElement.size(this.elementSize);
	    }
	    this.previewElement.attr(this.attributes);
	    this.previewElement.transform({rotation: this.elementRotation});
	},

	renderExternalGraphics: function () {
	    console.log('External graphics are not yet supported');
	    //this.previewElement = this.preview.image(this.xlink, this.elementSize);
	    //this.previewElement.center(75, 75);
	    //this.previewElement.transform({rotation: this.elementRotation});
	},

	renderLine: function (params, previewSize) {
	    this.preview.clear();
	    for (var i=0; i < params.length; i++) {
	      var attribute = params[i].attributeName;
	      var attributeValue = params[i].value;
	      if (attribute !== "stroke-width") {
	        this.attributes[attribute] = attributeValue;
	      }
	    }
	    if (previewSize	=== "small") {
	    	this.previewElement = this.preview.line(10, 10, 25, 25).stroke({width: 4});
	    } else {
	    	this.previewElement = this.preview.line(10, 10, 60, 60).stroke({width: 8});
	    }
	    this.previewElement.attr(this.attributes);
	},

	renderPolygon: function (params, previewSize) {
	    this.preview.clear();
	    var strokeWidth = false;
	    for (var i=0; i < params.length; i++) {
	      var attribute = params[i].attributeName;
	      var attributeValue = params[i].value;
	      if (attribute === "stroke-width") {
	        strokeWidth = true
	      } else {
	        this.attributes[attribute] = attributeValue;
	      }
	    }

	    if (previewSize	=== "small") {
	    	var elementWidth = 25;
	    	var elementHeight = 25;
	    } else {
	    	var elementWidth = 60;
	    	var elementHeight = 60;
	    }
	    this.previewElement = this.preview.rect(elementWidth,elementHeight).fill('none');
	    this.previewElement.attr(this.attributes);
	    if (strokeWidth === true) {
	      if (previewSize === "small") {
	        this.previewElement.attr({"stroke-width": 1});
	      } else {
	        this.previewElement.attr({"stroke-width": 4});
	      }
	    }
	},

	renderText: function (params, previewSize) {
	    this.preview.clear();
	    for (i=0; i < params.length; i++) {
	      var attribute = params[i].attributeName;
	      var attributeValue = params[i].value;
	      if (attribute === "font-family" || attribute === "font-style" || attribute === "font-weight" || attribute === "fill" || attribute === "halo-color" || attribute === "halo-radius") {
	        this.attributes[attribute] = attributeValue;
	      }
	    }

	    if (previewSize === "small") {
	    	this.previewElement = this.preview.text("Text!").font({size: 15});
	    } else {
	    	this.previewElement = this.preview.text("Text!").font({size: 30});
	   	}
	    this.previewElement.attr(this.attributes);
	},

	updatePreview: function () {
	    if (_.has(this.attributes, "stroke") && (this.symbolType === "pointsymbolizer" || this.symbolType === "polygonsymbolizer")) {
	      this.attributes["stroke-width"] = 3;
	    } else if (_.has(this.attributes, "size")) {
	    	this.attributes["size"] = 50;
	    }
	    this.previewElement.attr(this.attributes);
	},

	setSymbolType: function (symbolType) {
		this.symbolType = symbolType;
	},

	setSymbolizerId: function (id) {
		this.symbolizerId = id;
	},

	setParam: function(event) {
      var element,
          newvalue,
          param_id,
          param_css_parameter,
          validation;

      var param_id = null;
      var param_css_parameter = null;

      if (event.target.id === "graphic-symbol") {
        element = $(event.target)[0];//.find("#graphic-symbol");
        newvalue = element.value.toLowerCase();

        //if user leaves url invalid and changes the symbol we want to clear url
        if ($("#external-graphic").hasClass("invalid")) {
          var externalGraphicElement = $("#external-graphic")[0];
          externalGraphicElement.value = "";
          validateFunctions.handleValidParam(externalGraphicElement);
        }
        param_id = "" + element.dataset['paramId'];
        param_name = "graphic-symbol";
        validation = validateFunctions.validateParam(element, param_name, newvalue);
        if (validation[0] === "invalid") {
          validateFunctions.handleInvalidParam(validation, element);
          return;
        } else {
          if ($(element).hasClass("invalid")) {
            validateFunctions.handleValidParam(element);
          }
          this.renderWellKnownName(newvalue);
          this.dispatcher.trigger("updateMapStyleByParam",[{'name':'wellknownname','value': newvalue}],this.symbolType, this.symbolizerId);
        }
      } else {
        element = $(event.currentTarget).find(".form-control")[0];
        param_id = "" + element.dataset['paramId'];
        param_css_parameter = element.dataset['cssParameter'];
        newvalue = element.value;
        validation = validateFunctions.validateParam(element, param_css_parameter, newvalue);
        if (validation[0] === "invalid") {
          validateFunctions.handleInvalidParam(validation, element);
          return;
        }
        if ($(element).hasClass("invalid")) {
          validateFunctions.handleValidParam(element);
        }
        if (param_css_parameter === "rotation") {
          this.elementRotation = newvalue;
          this.previewElement.transform({rotation: this.elementRotation});
        // we don't want preview to update stroke-width
        } else if (param_css_parameter === "stroke-width") {
          //in case we need this later
          this.strokeWidth = parseInt(newvalue);
        } else if (param_css_parameter === "font-size") {
          //in case we need this later
          this.textSize = parseInt(newvalue);
        } else if (param_css_parameter === "stroke-dasharray-part") {
          newvalue = jQuery('input#stroke-dasharray-length').val()+' '+jQuery('input#stroke-dasharray-space').val();
          this.attributes["stroke-dasharray"] = newvalue;
          this.updatePreview();
        } else if (param_css_parameter !== "size") {
          this.attributes[param_css_parameter] = newvalue;
          this.updatePreview();
        }
        // Update map style
        this.dispatcher.trigger("updateMapStyleByParam",[{'name':param_css_parameter,'value': newvalue}], this.activeSymbolizer.type, this.activeSymbolizer.id );
      }

      this.dispatcher.trigger("updateModelParam", param_id, newvalue);
    }
	});

  	return SLDLegendView;

});<|endoftext|>


<commit_before>import { remote } from 'electron';
import { EventEmitter } from 'events';
import path from 'path';
import i18n from '../i18n/index.js';

const { Tray, Menu, app, getCurrentWindow, systemPreferences } = remote;

const mainWindow = getCurrentWindow();

const getTrayIconFileNameSuffix = ({ badge: { title, count, showAlert } }) => {
	if (title === '•') {
		return 'dot';
	} else if (count > 9) {
		return '9plus';
	} else if (count > 0) {
		return String(count);
	} else if (showAlert) {
		return 'alert';
	} else {
		return 'Template';
	}
};

const getTrayIconPath = (state) => {
	const iconDir = {
		win32: 'windows',
		linux: 'linux',
		darwin: 'osx',
	}[process.platform];
	const fileName = `icon-tray-${ getTrayIconFileNameSuffix(state) }.${ process.platform === 'win32' ? 'ico' : 'png' }`;
	return path.join(__dirname, 'images', iconDir, fileName);
};

const getTrayIconTitle = ({ badge: { title, count, showAlert }, status }) => {
	// TODO: remove status icon from title, since ANSI codes disable title color's adaptiveness
	const isDarkMode = systemPreferences.getUserDefault('AppleInterfaceStyle', 'string') === 'Dark';

	const statusAnsiColor = {
		online: '32',
		away: '33',
		busy: '31',
		offline: isDarkMode ? '37' : '0',
	}[status];

	const badgeTitleAnsiColor = isDarkMode ? '37' : '0';

	const statusBulletString = status ? `\u001B[${ statusAnsiColor }m•\u001B[0m` : null;
	const badgeTitleString = (showAlert && count > 0) ? `\u001B[${ badgeTitleAnsiColor }m${ title }\u001B[0m` : null;

	return [statusBulletString, badgeTitleString].filter(Boolean).join(' ');
};

const createContextMenuTemplate = ({ isHidden }, events) => ([
	{
		label: isHidden ? i18n.__('Show') : i18n.__('Hide'),
		click: () => events.emit('setVisibility', !isHidden),
	},
	{
		label: i18n.__('Quit'),
		click: () => events.emit('quit'),
	},
]);

function createAppTray() {
	const _tray = new Tray(getTrayIconPath({ badge: { title:'', count:0, showAlert:false } }));
	_tray.setToolTip(app.getName());

	mainWindow.tray = _tray;

	const events = new EventEmitter();

	const updateContextMenu = () => {
		const state = {
			isHidden: !mainWindow.isMinimized() && !mainWindow.isVisible(),
		};
		const template = createContextMenuTemplate(state, events);
		const menu = Menu.buildFromTemplate(template);
		_tray.setContextMenu(menu);
	};

	events.on('setVisibility', (visible) => {
		visible ? mainWindow.hide() : mainWindow.show();
		updateContextMenu();
	});

	events.on('quit', () => app.quit());

	mainWindow.on('hide', updateContextMenu);
	mainWindow.on('show', updateContextMenu);

	mainWindow.on('minimize', updateContextMenu);
	mainWindow.on('restore', updateContextMenu);

	updateContextMenu();

	_tray.on('right-click', function(event, bounds) {
		_tray.popUpContextMenu(undefined, bounds);
	});

	_tray.on('click', () => {
		if (mainWindow.isVisible() && !mainWindow.isMinimized()) {
			return mainWindow.hide();
		}

		mainWindow.show();
	});

	mainWindow.destroyTray = function() {
		mainWindow.removeListener('hide', updateContextMenu);
		mainWindow.removeListener('show', updateContextMenu);
		mainWindow.removeListener('minimize', updateContextMenu);
		mainWindow.removeListener('restore', updateContextMenu);
		_tray.destroy();
		mainWindow.emit('tray-destroyed');
	};

	mainWindow.emit('tray-created');
}

let state = {
	badge: {
		title: '',
		count: 0,
		showAlert: false,
	},
	status: 'online',
};

function showTrayAlert(badge, status = 'online') {
	if (mainWindow.tray === null || mainWindow.tray === undefined) {
		return;
	}

	state = {
		...state,
		badge,
		status,
	};

	const trayDisplayed = localStorage.getItem('hideTray') !== 'true';
	const statusDisplayed = (localStorage.getItem('showUserStatusInTray') || 'true') === 'true';
	const hasMentions = badge.showAlert && badge.count > 0;

	if (!mainWindow.isFocused()) {
		mainWindow.flashFrame(hasMentions);
	}

	if (process.platform === 'win32') {
		if (hasMentions) {
			mainWindow.webContents.send('render-taskbar-icon', badge.count);
		} else {
			mainWindow.setOverlayIcon(null, '');
		}
	}

	if (process.platform === 'darwin') {
		app.dock.setBadge(badge.title);
		if (trayDisplayed) {
			mainWindow.tray.setTitle(getTrayIconTitle({ badge, status: statusDisplayed && status }));
		}
	}

	if (process.platform === 'linux') {
		app.setBadgeCount(badge.count);
	}

	if (trayDisplayed) {
		mainWindow.tray.setImage(getTrayIconPath({ badge }));
	}
}

function removeAppTray() {
	mainWindow.destroyTray();
}

function toggle() {
	if (localStorage.getItem('hideTray') === 'true') {
		createAppTray();
		localStorage.setItem('hideTray', 'false');
		showTrayAlert(state.badge, state.status);
	} else {
		removeAppTray();
		localStorage.setItem('hideTray', 'true');
	}
}

function toggleStatus() {
	if (localStorage.getItem('showUserStatusInTray') === 'true') {
		localStorage.setItem('showUserStatusInTray', 'false');
	} else {
		localStorage.setItem('showUserStatusInTray', 'true');
	}

	if (localStorage.getItem('hideTray') !== 'true') {
		showTrayAlert(state.badge, state.status);
	}
}

if (localStorage.getItem('hideTray') !== 'true') {
	createAppTray();
}

export default {
	showTrayAlert,
	toggle,
	toggleStatus,
};
<commit_msg>Keep tray icon instance under tray module<commit_after>import { remote } from 'electron';
import { EventEmitter } from 'events';
import path from 'path';
import i18n from '../i18n/index.js';

const { Tray, Menu, app, getCurrentWindow, systemPreferences } = remote;

let trayIcon = null;

const getTrayIconFileNameSuffix = ({ badge: { title, count, showAlert } }) => {
	if (title === '•') {
		return 'dot';
	} else if (count > 9) {
		return '9plus';
	} else if (count > 0) {
		return String(count);
	} else if (showAlert) {
		return 'alert';
	} else {
		return 'Template';
	}
};

const getTrayIconPath = (state) => {
	const iconDir = {
		win32: 'windows',
		linux: 'linux',
		darwin: 'osx',
	}[process.platform];
	const fileName = `icon-tray-${ getTrayIconFileNameSuffix(state) }.${ process.platform === 'win32' ? 'ico' : 'png' }`;
	return path.join(__dirname, 'images', iconDir, fileName);
};

const getTrayIconTitle = ({ badge: { title, count, showAlert }, status }) => {
	// TODO: remove status icon from title, since ANSI codes disable title color's adaptiveness
	const isDarkMode = systemPreferences.getUserDefault('AppleInterfaceStyle', 'string') === 'Dark';

	const statusAnsiColor = {
		online: '32',
		away: '33',
		busy: '31',
		offline: isDarkMode ? '37' : '0',
	}[status];

	const badgeTitleAnsiColor = isDarkMode ? '37' : '0';

	const statusBulletString = status ? `\u001B[${ statusAnsiColor }m•\u001B[0m` : null;
	const badgeTitleString = (showAlert && count > 0) ? `\u001B[${ badgeTitleAnsiColor }m${ title }\u001B[0m` : null;

	return [statusBulletString, badgeTitleString].filter(Boolean).join(' ');
};

const createContextMenuTemplate = ({ isHidden }, events) => ([
	{
		label: isHidden ? i18n.__('Show') : i18n.__('Hide'),
		click: () => events.emit('setVisibility', !isHidden),
	},
	{
		label: i18n.__('Quit'),
		click: () => events.emit('quit'),
	},
]);

const mainWindow = getCurrentWindow();

function createAppTray() {
	trayIcon = new Tray(getTrayIconPath({ badge: { title:'', count:0, showAlert:false } }));
	trayIcon.setToolTip(app.getName());

	const events = new EventEmitter();

	const updateContextMenu = () => {
		const state = {
			isHidden: !mainWindow.isMinimized() && !mainWindow.isVisible(),
		};
		const template = createContextMenuTemplate(state, events);
		const menu = Menu.buildFromTemplate(template);
		trayIcon.setContextMenu(menu);
	};

	events.on('setVisibility', (visible) => {
		visible ? mainWindow.hide() : mainWindow.show();
		updateContextMenu();
	});

	events.on('quit', () => app.quit());

	mainWindow.on('hide', updateContextMenu);
	mainWindow.on('show', updateContextMenu);

	mainWindow.on('minimize', updateContextMenu);
	mainWindow.on('restore', updateContextMenu);

	updateContextMenu();

	trayIcon.on('right-click', function(event, bounds) {
		trayIcon.popUpContextMenu(undefined, bounds);
	});

	trayIcon.on('click', () => {
		if (mainWindow.isVisible() && !mainWindow.isMinimized()) {
			return mainWindow.hide();
		}

		mainWindow.show();
	});

	mainWindow.destroyTray = function() {
		mainWindow.removeListener('hide', updateContextMenu);
		mainWindow.removeListener('show', updateContextMenu);
		mainWindow.removeListener('minimize', updateContextMenu);
		mainWindow.removeListener('restore', updateContextMenu);
		trayIcon.destroy();
		mainWindow.emit('tray-destroyed');
	};

	mainWindow.emit('tray-created');
}

let state = {
	badge: {
		title: '',
		count: 0,
		showAlert: false,
	},
	status: 'online',
};

function showTrayAlert(badge, status = 'online') {
	if (trayIcon === null || trayIcon === undefined) {
		return;
	}

	state = {
		...state,
		badge,
		status,
	};

	const trayDisplayed = localStorage.getItem('hideTray') !== 'true';
	const statusDisplayed = (localStorage.getItem('showUserStatusInTray') || 'true') === 'true';
	const hasMentions = badge.showAlert && badge.count > 0;

	if (!mainWindow.isFocused()) {
		mainWindow.flashFrame(hasMentions);
	}

	if (process.platform === 'win32') {
		if (hasMentions) {
			mainWindow.webContents.send('render-taskbar-icon', badge.count);
		} else {
			mainWindow.setOverlayIcon(null, '');
		}
	}

	if (process.platform === 'darwin') {
		app.dock.setBadge(badge.title);
		if (trayDisplayed) {
			trayIcon.setTitle(getTrayIconTitle({ badge, status: statusDisplayed && status }));
		}
	}

	if (process.platform === 'linux') {
		app.setBadgeCount(badge.count);
	}

	if (trayDisplayed) {
		trayIcon.setImage(getTrayIconPath({ badge }));
	}
}

function removeAppTray() {
	mainWindow.destroyTray();
}

function toggle() {
	if (localStorage.getItem('hideTray') === 'true') {
		createAppTray();
		localStorage.setItem('hideTray', 'false');
		showTrayAlert(state.badge, state.status);
	} else {
		removeAppTray();
		localStorage.setItem('hideTray', 'true');
	}
}

function toggleStatus() {
	if (localStorage.getItem('showUserStatusInTray') === 'true') {
		localStorage.setItem('showUserStatusInTray', 'false');
	} else {
		localStorage.setItem('showUserStatusInTray', 'true');
	}

	if (localStorage.getItem('hideTray') !== 'true') {
		showTrayAlert(state.badge, state.status);
	}
}

if (localStorage.getItem('hideTray') !== 'true') {
	createAppTray();
}

export default {
	showTrayAlert,
	toggle,
	toggleStatus,
};
<|endoftext|>


<commit_before>import {PLATFORM} from 'aurelia-pal';
import {initialize} from 'aurelia-pal-browser';

let bootstrapQueue = [];
let sharedLoader = null;
let Aurelia = null;

function onBootstrap(callback) {
  return new Promise((resolve, reject) => {
    if (sharedLoader) {
      resolve(callback(sharedLoader));
    } else {
      bootstrapQueue.push(() => {
        try {
          resolve(callback(sharedLoader));
        } catch (e) {
          reject(e);
        }
      });
    }
  });
}

function ready(global) {
  return new Promise((resolve, reject) => {
    if (global.document.readyState === 'complete') {
      resolve(global.document);
    } else {
      global.document.addEventListener('DOMContentLoaded', completed);
      global.addEventListener('load', completed);
    }

    function completed() {
      global.document.removeEventListener('DOMContentLoaded', completed);
      global.removeEventListener('load', completed);
      resolve(global.document);
    }
  });
}

function createLoader() {
  if (PLATFORM.Loader) {
    return Promise.resolve(new PLATFORM.Loader());
  }

  if (window.System && typeof window.System.import === 'function') {
    return System.normalize('aurelia-bootstrapper').then(bootstrapperName => {
      return System.normalize('aurelia-loader-default', bootstrapperName);
    }).then(loaderName => {
      return System.import(loaderName).then(m => new m.DefaultLoader());
    });
  }

  if (typeof window.require === 'function') {
    return new Promise((resolve, reject) => require(['aurelia-loader-default'], m => resolve(new m.DefaultLoader()), reject));
  }

  return Promise.reject('No PLATFORM.Loader is defined and there is neither a System API (ES6) or a Require API (AMD) globally available to load your app.');
}

function preparePlatform(loader) {
  return loader.normalize('aurelia-bootstrapper').then(bootstrapperName => {
    return loader.normalize('aurelia-framework', bootstrapperName).then(frameworkName => {
      loader.map('aurelia-framework', frameworkName);

      return Promise.all([
        loader.normalize('aurelia-dependency-injection', frameworkName)
          .then(diName => loader.map('aurelia-dependency-injection', diName)),
        loader.normalize('aurelia-router', bootstrapperName)
          .then(routerName => loader.map('aurelia-router', routerName)),
        loader.normalize('aurelia-logging-console', bootstrapperName)
          .then(loggingConsoleName => loader.map('aurelia-logging-console', loggingConsoleName))
      ]).then(() => {
        return loader.loadModule(frameworkName).then(m => Aurelia = m.Aurelia);
      });
    });
  });
}

function handleApp(loader, appHost) {
  return config(loader, appHost, appHost.getAttribute('aurelia-app'));
}

function config(loader, appHost, configModuleId) {
  const aurelia = new Aurelia(loader);
  aurelia.host = appHost;

  if (configModuleId) {
    return loader.loadModule(configModuleId).then(customConfig => customConfig.configure(aurelia));
  }

  aurelia.use
    .standardConfiguration()
    .developmentLogging();

  return aurelia.start().then(() => aurelia.setRoot());
}

function run() {
  return ready(window).then(doc => {
    initialize();

    const appHost = doc.querySelectorAll('[aurelia-app]');
    return createLoader().then(loader => {
      return preparePlatform(loader).then(() => {
        for (let i = 0, ii = appHost.length; i < ii; ++i) {
          handleApp(loader, appHost[i]).catch(console.error.bind(console));
        }

        sharedLoader = loader;
        for (let i = 0, ii = bootstrapQueue.length; i < ii; ++i) {
          bootstrapQueue[i]();
        }
        bootstrapQueue = null;
      });
    });
  });
}

/**
 * Manually bootstraps an application.
 * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app.
 * @return A Promise that completes when configuration is done.
 */
export function bootstrap(configure: Function): Promise<void> {
  return onBootstrap(loader => {
    const aurelia = new Aurelia(loader);
    return configure(aurelia);
  });
}

run();
<commit_msg>feat(index): use aurelia polyfills<commit_after>import 'aurelia-polyfills';
import {PLATFORM} from 'aurelia-pal';
import {initialize} from 'aurelia-pal-browser';

let bootstrapQueue = [];
let sharedLoader = null;
let Aurelia = null;

function onBootstrap(callback) {
  return new Promise((resolve, reject) => {
    if (sharedLoader) {
      resolve(callback(sharedLoader));
    } else {
      bootstrapQueue.push(() => {
        try {
          resolve(callback(sharedLoader));
        } catch (e) {
          reject(e);
        }
      });
    }
  });
}

function ready(global) {
  return new Promise((resolve, reject) => {
    if (global.document.readyState === 'complete') {
      resolve(global.document);
    } else {
      global.document.addEventListener('DOMContentLoaded', completed);
      global.addEventListener('load', completed);
    }

    function completed() {
      global.document.removeEventListener('DOMContentLoaded', completed);
      global.removeEventListener('load', completed);
      resolve(global.document);
    }
  });
}

function createLoader() {
  if (PLATFORM.Loader) {
    return Promise.resolve(new PLATFORM.Loader());
  }

  if (window.System && typeof window.System.import === 'function') {
    return System.normalize('aurelia-bootstrapper').then(bootstrapperName => {
      return System.normalize('aurelia-loader-default', bootstrapperName);
    }).then(loaderName => {
      return System.import(loaderName).then(m => new m.DefaultLoader());
    });
  }

  if (typeof window.require === 'function') {
    return new Promise((resolve, reject) => require(['aurelia-loader-default'], m => resolve(new m.DefaultLoader()), reject));
  }

  return Promise.reject('No PLATFORM.Loader is defined and there is neither a System API (ES6) or a Require API (AMD) globally available to load your app.');
}

function preparePlatform(loader) {
  return loader.normalize('aurelia-bootstrapper').then(bootstrapperName => {
    return loader.normalize('aurelia-framework', bootstrapperName).then(frameworkName => {
      loader.map('aurelia-framework', frameworkName);

      return Promise.all([
        loader.normalize('aurelia-dependency-injection', frameworkName)
          .then(diName => loader.map('aurelia-dependency-injection', diName)),
        loader.normalize('aurelia-router', bootstrapperName)
          .then(routerName => loader.map('aurelia-router', routerName)),
        loader.normalize('aurelia-logging-console', bootstrapperName)
          .then(loggingConsoleName => loader.map('aurelia-logging-console', loggingConsoleName))
      ]).then(() => {
        return loader.loadModule(frameworkName).then(m => Aurelia = m.Aurelia);
      });
    });
  });
}

function handleApp(loader, appHost) {
  return config(loader, appHost, appHost.getAttribute('aurelia-app'));
}

function config(loader, appHost, configModuleId) {
  const aurelia = new Aurelia(loader);
  aurelia.host = appHost;

  if (configModuleId) {
    return loader.loadModule(configModuleId).then(customConfig => customConfig.configure(aurelia));
  }

  aurelia.use
    .standardConfiguration()
    .developmentLogging();

  return aurelia.start().then(() => aurelia.setRoot());
}

function run() {
  return ready(window).then(doc => {
    initialize();

    const appHost = doc.querySelectorAll('[aurelia-app]');
    return createLoader().then(loader => {
      return preparePlatform(loader).then(() => {
        for (let i = 0, ii = appHost.length; i < ii; ++i) {
          handleApp(loader, appHost[i]).catch(console.error.bind(console));
        }

        sharedLoader = loader;
        for (let i = 0, ii = bootstrapQueue.length; i < ii; ++i) {
          bootstrapQueue[i]();
        }
        bootstrapQueue = null;
      });
    });
  });
}

/**
 * Manually bootstraps an application.
 * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app.
 * @return A Promise that completes when configuration is done.
 */
export function bootstrap(configure: Function): Promise<void> {
  return onBootstrap(loader => {
    const aurelia = new Aurelia(loader);
    return configure(aurelia);
  });
}

run();
<|endoftext|>


<commit_before>/*global define*/
define([
	'jQuery-2.1.4.min',
	'underscore_1.3.3',
	'backbone_0.9.2',
  'bootstrap-table',
  '../model/FrontEndWidgetModel.js',
  'text!../../../js/app/templates/editWidget.html'
], function ($, _, Backbone, BootstrapTable, FrontEndWidgetModel, EditWidgetTemplate) {
	'use strict';

	var FrontEndWidgetView = Backbone.View.extend({

		tagName:  'div',

    el: '#widgetEditor',

		template: _.template(EditWidgetTemplate),

		// The DOM events specific to an item.
		events: {
      'click #addHTMLBtn': 'addHTML',
      'click #addImgRowBtn': 'addImgRow',
      //'click #addWidget': 'addWidget',
      'change #widgetTitle': 'updateWidget',
      'change #widgetDesc': 'updateWidget',
      'change .widgetText': 'updateWidget'
		},

		initialize: function () {
      this.targetWidget = -1; //Index that points to the currently loaded Widget.
      this.targetImage = -1; //Index that points to the currently selected image within a widget.
		},

    render: function (index) {
      debugger;

      this.$el.html(this.template);

      if(index != undefined) {
        this.model = global.frontEndWidgetCollection.models[index];

        this.targetWidget = index;
      }

      this.$el.find('#widgetTitle').val(this.model.get('title'));
      this.$el.find('#widgetDesc').val(this.model.get('desc'));

      var htmlArray = this.model.get('contentArray');
      var imgArray = this.model.get('imgUrlArray');

      //BEGIN POPULATION OF HTML ARRAY
      if((htmlArray.length == 0) || (htmlArray.length == undefined)) {
        //Do nothing. Leave the default HTML the way it is.
        //debugger;

        //On second though, do not need to do the below:
        //Add click handler to delete button above the HTML entry box.
        //this.$el.find('#widgetHTML').find('.scaffold').find('button').click([-1], this.deleteHtml);
      } else {

        for(var i=0; i < htmlArray.length; i++) {
          var tmpEntry = this.$el.find('#widgetHTML').find('.scaffold').clone(); //Clone the scaffolding
          var contentSelector = 'content'+i;

          tmpEntry.removeClass('scaffold'); //Remove the scaffold class
          tmpEntry.addClass(contentSelector);
          tmpEntry.find('button').click([i],this.deleteHtml); //Assign a click handler to the delete button
          tmpEntry.find('textarea').val(htmlArray[i]); //Populate the text box
          this.$el.find('#widgetHTML').append(tmpEntry);
        }

        //Remove the scaffolding element
        //this.$el.find('#widgetHTML').find('.scaffold').remove();
        //Hide the scaffolding element
        this.$el.find('#widgetHTML').find('.scaffold').hide();
      }
      //END POPULATION OF HTML ARRAY

      //BEGIN POPULATION OF IMAGE ARRAY
      if((imgArray.length == 0) || (imgArray.length == undefined)) {
        //Do nothing. Leave the default image layout the way it is.
        debugger;
      } else {

        //Initialize
        var colIndex = 0;
        var rowIndex = 0;
        var tmpRow = this.$el.find('#widgetImages').find('.scaffold').clone();
        tmpRow.removeClass('scaffold');

        //Loop through all the images
        for(var i=0; i < imgArray.length; i++) {
          var imgSelector = '.img'+colIndex;

          var thisImg = tmpRow.find(imgSelector);

          thisImg.find('button').click([i], this.deleteImg); //Assign a click handler to the delete button.

          thisImg.find('img').attr('onclick', ''); //Remove the default click handler.
          thisImg.find('img').click([i], this.swapImg); //Assign a click handler to the image to swap out the image with a new one.
          thisImg.find('img').attr('src', imgArray[i]); //Swap out the image with the one assigned to the widget.

          //Manage the column index
          colIndex++;
          if(colIndex > 2) {
            colIndex = 0;

            this.$el.find('#widgetImages').append(tmpRow);
            var tmpRow = this.$el.find('#widgetImages').find('.scaffold').clone();
            //tmpRow.find('.label').remove(); //Remove the label.
            tmpRow.removeClass('scaffold');

            //Corner case: if colIndex > 2 AND this is the last entry in imgArray
            if(i == imgArray.length-1) {
              return this;
            }
          }
        }

        //this.$el.find('#widgetImages').find('.scaffold').remove();
        this.$el.find('#widgetImages').append(tmpRow);
      }
      //END POPULATION OF IMAGE ARRAY

			return this;
		},

    //This function is called when the user clicks on the delete button assigned to an HTML array
    //element. It's purpose is to remove the array entry from the model.
    deleteHtml: function(event) {
      //debugger;

      //Get the contentArray from the model.
      //var this.model = global.frontEndWidgetCollection.models[global.editWidgetView.targetWidget];
      var thisModel = global.editWidgetView.model;
      var contentArray = thisModel.get('contentArray');

      //Create a handle for the parent

      var textDiv = $(event.target).parent();
      //Fixes bug where user clicks on fa icon instead of button.
      var textDivClass = textDiv.attr('class');
      if(textDivClass.indexOf('btn') != -1)
        textDiv = textDiv.parent();

      var isContent0 = (textDiv.attr('class').indexOf('content0') != -1);

      if(isContent0) {
        textDiv.find('textarea').val(''); //Clear the text area

        var contentArrayNotEmpty = ((contentArray[0] != "") && (contentArray[0] != undefined))

        //Delete contentArray element 0 if it exists
        if(contentArrayNotEmpty) {
          contentArray.splice(0,1);
          thisModel.set('contentArray', contentArray);

          thisModel.refreshWidget = true;
          thisModel.save();
        }
      } else {

        //Find out what contentArray element this text box is associated with
        var classIndex = textDiv.attr('class').indexOf('content');
        //I might consider adding error handling code here to test. if(classIndex == -1) error
        var contentClass = textDiv.attr('class').slice(classIndex);
        var contentIndex = Number(contentClass.slice(7));

        //Delete that element from the contentArray (if it exists)
        contentArray.splice(contentIndex,1);
        thisModel.set('contentArray', contentArray);

        //Rerender the UI
        thisModel.refreshWidget = true;
        thisModel.save();
      }

    },

    //This function is called when the user clicks on the delete button assigned to the image.
    //It's purpose is to remove the image from the current front end widget.
    deleteImg: function(index) {
      debugger;

      //If index is a click event object, then retrieve the data passed in.
      if(typeof(index) == "object")
        index = index.data[0];
    },

    swapImg: function(index) {
      debugger;

      //If index is a click event object, then retrieve the data passed in.
      if(typeof(index) == "object") {
        index = index.data[0];
        this.targetImage = index;
      }

      //Modal has exited and returns the URL to a selected image
      if(typeof(index) == "string") {
        var imgArray = this.model.get('imgUrlArray');

        //This should not happen
        if(this.targetImage == -1) {
          debugger;

        //An empty image was selected. A new image needs to be pushed into the array.
        } else if(imgArray.length <= this.targetImage) {
          imgArray.push(index);

        //An existing image was clicked and the selected image needs to replace it.
        } else {
          imgArray[this.targetImage] = index;
        }

        this.model.set('imgUrlArray', imgArray);
        this.model.refreshWidget = true;
        this.model.save();

        return;
      }

      if(index == -1) {
        console.log('Empty image was clicked');
      } else {
        console.log('Existing image was clicked');
      }

      global.modalView.browseImageLibrary();

    },

    //This function is called when the user clicks on the 'Add HTML' button.
    //This function adds a new textarea element to the DOM for additional content.
    addHTML: function() {
      //debugger;

      var contentArray = this.model.get('contentArray');
      var contentIndex = contentArray.length;

      var tmpEntry = this.$el.find('#widgetHTML').find('.scaffold').clone(); //Clone the scaffolding
      tmpEntry.removeClass('scaffold'); //Remove the scaffold class
      tmpEntry.addClass('content'+contentIndex);
      tmpEntry.find('button').click([contentIndex],this.deleteHtml); //Assign a click handler to the delete button
      this.$el.find('#widgetHTML').append(tmpEntry);
      tmpEntry.show();
    },

    //This function is called when the suer clicks on the 'Add Image Row' button.
    addImgRow: function() {
      debugger;
    },

    //This function gets called anytime any of the input fields are changed.
    //The purpose is to save data in an event-driven way and then sync those changes with the server.
    updateWidget: function(event) {
      //var thisModel = global.frontEndWidgetCollection.models[global.editWidgetView.targetWidget];

      this.model.set('title', this.$el.find('#widgetTitle').val());
      this.model.set('desc', this.$el.find('#widgetDesc').val());

      // BEGIN SAVING CONTENT ARRAY
      var contentArray = this.model.get('contentArray');

      var widgetTextElems = this.$el.find('.widgetText');
      var widgetTextDivs = widgetTextElems.parent();
      for(var i=0; i < widgetTextDivs.length; i++) {
        var thisElem = $(widgetTextDivs[i]);
        var thisClass = thisElem.attr('class');

        //Skip the scaffold element if there are other entries.
        if(thisElem.hasClass('scaffold')) {
          if(widgetTextDivs.length != 1) {
            continue;
          //If this is the only textarea, then save the content and break out of the loop.
          } else {
            var content = thisElem.find('textarea').val();
            contentArray.push(content);
            break;
          }
        }

        //Get the contentArray index this textarray element represents
        var classIndex = $(widgetTextDivs[i]).attr('class').indexOf('content');
        //I might consider adding error handling code here to test. if(classIndex == -1) error
        var contentClass = $(widgetTextDivs[i]).attr('class').slice(classIndex);
        var contentIndex = Number(contentClass.slice(7));

        var content = thisElem.find('textarea').val();

        if(contentIndex >= contentArray.length) {
          contentArray.push(content);
        } else {
          contentArray[contentIndex] = content;
        }

      }

      this.model.set('contentArray', contentArray);

      this.model.refreshWidget = true;
      this.model.save();
      // END SAVING CONTENT ARRAY

    }

	});

  //debugger;
	return FrontEndWidgetView;
});<commit_msg>Developing image library browsing in the modal view.<commit_after>/*global define*/
define([
	'jQuery-2.1.4.min',
	'underscore_1.3.3',
	'backbone_0.9.2',
  'bootstrap-table',
  '../model/FrontEndWidgetModel.js',
  'text!../../../js/app/templates/editWidget.html'
], function ($, _, Backbone, BootstrapTable, FrontEndWidgetModel, EditWidgetTemplate) {
	'use strict';

	var FrontEndWidgetView = Backbone.View.extend({

		tagName:  'div',

    el: '#widgetEditor',

		template: _.template(EditWidgetTemplate),

		// The DOM events specific to an item.
		events: {
      'click #addHTMLBtn': 'addHTML',
      'click #addImgRowBtn': 'addImgRow',
      //'click #addWidget': 'addWidget',
      'change #widgetTitle': 'updateWidget',
      'change #widgetDesc': 'updateWidget',
      'change .widgetText': 'updateWidget'
		},

		initialize: function () {
      this.targetWidget = -1; //Index that points to the currently loaded Widget.
      this.targetImage = -1; //Index that points to the currently selected image within a widget.
		},

    render: function (index) {
      debugger;

      this.$el.html(this.template);

      if(index != undefined) {
        this.model = global.frontEndWidgetCollection.models[index];

        this.targetWidget = index;
      }

      this.$el.find('#widgetTitle').val(this.model.get('title'));
      this.$el.find('#widgetDesc').val(this.model.get('desc'));

      var htmlArray = this.model.get('contentArray');
      var imgArray = this.model.get('imgUrlArray');

      //BEGIN POPULATION OF HTML ARRAY
      if((htmlArray.length == 0) || (htmlArray.length == undefined)) {
        //Do nothing. Leave the default HTML the way it is.
        //debugger;

        //On second though, do not need to do the below:
        //Add click handler to delete button above the HTML entry box.
        //this.$el.find('#widgetHTML').find('.scaffold').find('button').click([-1], this.deleteHtml);
      } else {

        for(var i=0; i < htmlArray.length; i++) {
          var tmpEntry = this.$el.find('#widgetHTML').find('.scaffold').clone(); //Clone the scaffolding
          var contentSelector = 'content'+i;

          tmpEntry.removeClass('scaffold'); //Remove the scaffold class
          tmpEntry.addClass(contentSelector);
          tmpEntry.find('button').click([i],this.deleteHtml); //Assign a click handler to the delete button
          tmpEntry.find('textarea').val(htmlArray[i]); //Populate the text box
          this.$el.find('#widgetHTML').append(tmpEntry);
        }

        //Remove the scaffolding element
        //this.$el.find('#widgetHTML').find('.scaffold').remove();
        //Hide the scaffolding element
        this.$el.find('#widgetHTML').find('.scaffold').hide();
      }
      //END POPULATION OF HTML ARRAY

      //BEGIN POPULATION OF IMAGE ARRAY
      if((imgArray.length == 0) || (imgArray.length == undefined)) {
        //Do nothing. Leave the default image layout the way it is.
        debugger;
      } else {

        //Initialize
        var colIndex = 0;
        var rowIndex = 0;
        var tmpRow = this.$el.find('#widgetImages').find('.scaffold').clone();
        tmpRow.removeClass('scaffold');

        //Loop through all the images
        for(var i=0; i < imgArray.length; i++) {
          var imgSelector = '.img'+colIndex;

          var thisImg = tmpRow.find(imgSelector);

          thisImg.find('button').click([i], this.deleteImg); //Assign a click handler to the delete button.

          thisImg.find('img').attr('onclick', ''); //Remove the default click handler.
          thisImg.find('img').click([i], this.swapImg); //Assign a click handler to the image to swap out the image with a new one.
          thisImg.find('img').attr('src', imgArray[i]); //Swap out the image with the one assigned to the widget.

          //Manage the column index
          colIndex++;
          if(colIndex > 2) {
            colIndex = 0;

            this.$el.find('#widgetImages').append(tmpRow);
            var tmpRow = this.$el.find('#widgetImages').find('.scaffold').clone();
            //tmpRow.find('.label').remove(); //Remove the label.
            tmpRow.removeClass('scaffold');

            //Corner case: if colIndex > 2 AND this is the last entry in imgArray
            if(i == imgArray.length-1) {
              this.$el.find('#widgetImages').find('.scaffold').hide();
              return this;
            }
          }
        }

        //this.$el.find('#widgetImages').find('.scaffold').remove();
        this.$el.find('#widgetImages').append(tmpRow);

        this.$el.find('#widgetImages').find('.scaffold').hide();
      }
      //END POPULATION OF IMAGE ARRAY

			return this;
		},

    //This function is called when the user clicks on the delete button assigned to an HTML array
    //element. It's purpose is to remove the array entry from the model.
    deleteHtml: function(event) {
      //debugger;

      //Get the contentArray from the model.
      //var this.model = global.frontEndWidgetCollection.models[global.editWidgetView.targetWidget];
      var thisModel = global.editWidgetView.model;
      var contentArray = thisModel.get('contentArray');

      //Create a handle for the parent

      var textDiv = $(event.target).parent();
      //Fixes bug where user clicks on fa icon instead of button.
      var textDivClass = textDiv.attr('class');
      if(textDivClass.indexOf('btn') != -1)
        textDiv = textDiv.parent();

      var isContent0 = (textDiv.attr('class').indexOf('content0') != -1);

      if(isContent0) {
        textDiv.find('textarea').val(''); //Clear the text area

        var contentArrayNotEmpty = ((contentArray[0] != "") && (contentArray[0] != undefined))

        //Delete contentArray element 0 if it exists
        if(contentArrayNotEmpty) {
          contentArray.splice(0,1);
          thisModel.set('contentArray', contentArray);

          thisModel.refreshWidget = true;
          thisModel.save();
        }
      } else {

        //Find out what contentArray element this text box is associated with
        var classIndex = textDiv.attr('class').indexOf('content');
        //I might consider adding error handling code here to test. if(classIndex == -1) error
        var contentClass = textDiv.attr('class').slice(classIndex);
        var contentIndex = Number(contentClass.slice(7));

        //Delete that element from the contentArray (if it exists)
        contentArray.splice(contentIndex,1);
        thisModel.set('contentArray', contentArray);

        //Rerender the UI
        thisModel.refreshWidget = true;
        thisModel.save();
      }

    },

    //This function is called when the user clicks on the delete button assigned to the image.
    //It's purpose is to remove the image from the current front end widget.
    deleteImg: function(index) {
      debugger;

      //If index is a click event object, then retrieve the data passed in.
      if(typeof(index) == "object")
        index = index.data[0];
    },

    swapImg: function(index) {
      debugger;

      //If index is a click event object, then retrieve the data passed in.
      if(typeof(index) == "object") {
        index = index.data[0];
        this.targetImage = index;
      }

      //Modal has exited and returns the URL to a selected image
      if(typeof(index) == "string") {
        var imgArray = this.model.get('imgUrlArray');

        //This should not happen
        if(this.targetImage == -1) {
          debugger;

        //An empty image was selected. A new image needs to be pushed into the array.
        } else if(imgArray.length <= this.targetImage) {
          imgArray.push(index);

        //An existing image was clicked and the selected image needs to replace it.
        } else {
          imgArray[this.targetImage] = index;
        }

        this.model.set('imgUrlArray', imgArray);
        this.model.refreshWidget = true;
        this.model.save();

        return;
      }

      if(index == -1) {
        console.log('Empty image was clicked');
      } else {
        console.log('Existing image was clicked');
      }

      global.modalView.browseImageLibrary();

    },

    //This function is called when the user clicks on the 'Add HTML' button.
    //This function adds a new textarea element to the DOM for additional content.
    addHTML: function() {
      //debugger;

      var contentArray = this.model.get('contentArray');
      var contentIndex = contentArray.length;

      var tmpEntry = this.$el.find('#widgetHTML').find('.scaffold').clone(); //Clone the scaffolding
      tmpEntry.removeClass('scaffold'); //Remove the scaffold class
      tmpEntry.addClass('content'+contentIndex);
      tmpEntry.find('button').click([contentIndex],this.deleteHtml); //Assign a click handler to the delete button
      this.$el.find('#widgetHTML').append(tmpEntry);
      tmpEntry.show();
    },

    //This function is called when the suer clicks on the 'Add Image Row' button.
    addImgRow: function() {
      debugger;
    },

    //This function gets called anytime any of the input fields are changed.
    //The purpose is to save data in an event-driven way and then sync those changes with the server.
    updateWidget: function(event) {
      //var thisModel = global.frontEndWidgetCollection.models[global.editWidgetView.targetWidget];

      this.model.set('title', this.$el.find('#widgetTitle').val());
      this.model.set('desc', this.$el.find('#widgetDesc').val());

      // BEGIN SAVING CONTENT ARRAY
      var contentArray = this.model.get('contentArray');

      var widgetTextElems = this.$el.find('.widgetText');
      var widgetTextDivs = widgetTextElems.parent();
      for(var i=0; i < widgetTextDivs.length; i++) {
        var thisElem = $(widgetTextDivs[i]);
        var thisClass = thisElem.attr('class');

        //Skip the scaffold element if there are other entries.
        if(thisElem.hasClass('scaffold')) {
          if(widgetTextDivs.length != 1) {
            continue;
          //If this is the only textarea, then save the content and break out of the loop.
          } else {
            var content = thisElem.find('textarea').val();
            contentArray.push(content);
            break;
          }
        }

        //Get the contentArray index this textarray element represents
        var classIndex = $(widgetTextDivs[i]).attr('class').indexOf('content');
        //I might consider adding error handling code here to test. if(classIndex == -1) error
        var contentClass = $(widgetTextDivs[i]).attr('class').slice(classIndex);
        var contentIndex = Number(contentClass.slice(7));

        var content = thisElem.find('textarea').val();

        if(contentIndex >= contentArray.length) {
          contentArray.push(content);
        } else {
          contentArray[contentIndex] = content;
        }

      }

      this.model.set('contentArray', contentArray);

      this.model.refreshWidget = true;
      this.model.save();
      // END SAVING CONTENT ARRAY

    }

	});

  //debugger;
	return FrontEndWidgetView;
});<|endoftext|>


<commit_before>'use strict';

import { remote } from 'electron';
import path from 'path';

var Tray = remote.Tray;
var Menu = remote.Menu;

let mainWindow = remote.getCurrentWindow();

var icons = {
    win32: {
        dir: 'windows'
    },

    linux: {
        dir: 'linux'
    },

    darwin: {
        dir: 'osx',
        icon: 'icon-trayTemplate.png'
    }
};

let _iconTray = path.join(__dirname, 'images', icons[process.platform].dir, icons[process.platform].icon || 'icon-tray.png');
let _iconTrayAlert = path.join(__dirname, 'images', icons[process.platform].dir, icons[process.platform].iconAlert || 'icon-tray-alert.png');

function createAppTray () {
    let _tray = new Tray(_iconTray);
    var contextMenu = Menu.buildFromTemplate([{
        label: 'Quit',
        click: function () {
            remote.app.quit();
        }

    }]);

    _tray.setToolTip(remote.app.getName());

    _tray.on('right-click', function (e, b) {
        _tray.popUpContextMenu(contextMenu, b);
    });

    _tray.on('click', function (e, b) {
        if (e.ctrlKey === true) {
            _tray.popUpContextMenu(contextMenu, b);
            return;
        }

        if (mainWindow.isVisible()) {
            mainWindow.hide();
        } else {
            mainWindow.show();
        }
    });

    mainWindow = mainWindow;
    mainWindow.tray = _tray;
}

function showTrayAlert (showAlert, title) {
    if (mainWindow.tray === null || mainWindow.tray === undefined) {
        return;
    }

    mainWindow.flashFrame(showAlert);
    if (showAlert) {
        mainWindow.tray.setImage(_iconTrayAlert);
        if (process.platform === 'darwin') {
            mainWindow.tray.setTitle(title);
        }
    } else {
        mainWindow.tray.setImage(_iconTray);
        if (process.platform === 'darwin') {
            mainWindow.tray.setTitle(title);
        }
    }
}

createAppTray();

export default {
    showTrayAlert: showTrayAlert
};
<commit_msg>Fix try context menu<commit_after>'use strict';

import { remote } from 'electron';
import path from 'path';

const { Tray, Menu } = remote;

const mainWindow = remote.getCurrentWindow();

const icons = {
    win32: {
        dir: 'windows'
    },

    linux: {
        dir: 'linux'
    },

    darwin: {
        dir: 'osx',
        icon: 'icon-trayTemplate.png'
    }
};

const _iconTray = path.join(__dirname, 'images', icons[process.platform].dir, icons[process.platform].icon || 'icon-tray.png');
const _iconTrayAlert = path.join(__dirname, 'images', icons[process.platform].dir, icons[process.platform].iconAlert || 'icon-tray-alert.png');

function createAppTray () {
    const _tray = new Tray(_iconTray);
    const contextMenuShow = Menu.buildFromTemplate([{
        label: 'Show',
        click () {
            mainWindow.show();
        }
    }, {
        label: 'Quit',
        click () {
            remote.app.quit();
        }
    }]);

    const contextMenuHide = Menu.buildFromTemplate([{
        label: 'Hide',
        click () {
            mainWindow.hide();
        }
    }, {
        label: 'Quit',
        click () {
            remote.app.quit();
        }
    }]);

    if (!mainWindow.isMinimized() && !mainWindow.isVisible()) {
        _tray.setContextMenu(contextMenuShow);
    } else {
        _tray.setContextMenu(contextMenuHide);
    }

    mainWindow.on('show', () => {
        _tray.setContextMenu(contextMenuHide);
    });

    mainWindow.on('hide', () => {
        _tray.setContextMenu(contextMenuShow);
    });

    _tray.setToolTip(remote.app.getName());

    _tray.on('right-click', function (e, b) {
        _tray.popUpContextMenu(undefined, b);
    });

    mainWindow.tray = _tray;
}

function showTrayAlert (showAlert, title) {
    if (mainWindow.tray === null || mainWindow.tray === undefined) {
        return;
    }

    mainWindow.flashFrame(showAlert);
    if (showAlert) {
        mainWindow.tray.setImage(_iconTrayAlert);
        if (process.platform === 'darwin') {
            mainWindow.tray.setTitle(title);
        }
    } else {
        mainWindow.tray.setImage(_iconTray);
        if (process.platform === 'darwin') {
            mainWindow.tray.setTitle(title);
        }
    }
}

createAppTray();

export default {
    showTrayAlert: showTrayAlert
};
<|endoftext|>


<commit_before>$(document).on('pageload', function(){
	$('.parallax').parallax();
	$('.tabs').tabs();
	$('.dropdown-button').dropdown();
});

void function(){
	$(document).trigger('pageload', this.href);

	if (!('pushState' in window.history)) return;

	var $body = $('body'),
		$page = $('.current-page');

	function load(url) {
		console.log('Load', url);

    $('.unloading').remove();
		$page.addClass('unloading');

		$page = $('<div/>').appendTo($body);

		$page.load(url + ' main', function() {
			$(document).trigger('pageload', this.href);
			$body.scrollTop(0);
		});
	}

	$body.on('click', 'a', function(event){
		var href = this.getAttribute('href');
		if (href.indexOf('#') == 0 || href.indexOf(':') >= 0) return;

		href = this.href;

		console.log('pushState', href);
		window.history.pushState({push:true}, '', href);

		load(href);

		event.preventDefault();
	});

	window.onpopstate = function(event) {
		console.log('Popstate', location.href);
		if (event.state && event.state.push) {
			load(location.href);
		}
	};

}();

$('body').on('submit', 'form', function(){
	alert('Sistema de testes, desabilitei o submit do cartão, claro');
	return false;
});

// new Notification('Pagamento confirmado', {icon: 'img/icon.png', body: 'Saída liberada até ' + (new Date().getHours()+1) + 'h'})

if ('serviceWorker' in navigator && (window.location.protocol === 'https:' || window.location.hostname === 'localhost')) {
  navigator.serviceWorker.register('./service-worker.js', {
    scope: './'
  }).then(function(registration) {
    // Check to see if there's an updated version of service-worker.js with new files to cache:
    // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-registration-update-method
    if (typeof registration.update == 'function') {
      registration.update();
    }

    // updatefound is fired if service-worker.js changes.
    registration.onupdatefound = function() {
      // The updatefound event implies that registration.installing is set; see
      // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
      var installingWorker = registration.installing;

      installingWorker.onstatechange = function() {
        switch (installingWorker.state) {
          case 'installed':
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and the fresh content will
              // have been added to the cache.
              // It's the perfect time to display a "New content is available; please refresh."
              // message in the page's interface.
              console.log('New or updated content is available.');
              location.reload();
            } else {
              // At this point, everything has been precached, but the service worker is not
              // controlling the page. The service worker will not take control until the next
              // reload or navigation to a page under the registered scope.
              // It's the perfect time to display a "Content is cached for offline use." message.
              console.log('Content is cached, and will be available for offline use the ' +
                          'next time the page is loaded.')
            }
          break;

          case 'redundant':
            console.error('The installing service worker became redundant.');
          break;
        }
      };
    };
  }).catch(function(e) {
    console.error('Error during service worker registration:', e);
  });
}<commit_msg>Update 2015-08-21T20:55:32.175Z<commit_after>$(document).on('pageload', function(){
	$('.parallax').parallax();
	$('.tabs').tabs();
	$('.dropdown-button').dropdown();
});

void function(){
	$(document).trigger('pageload', this.href);

	if (!('pushState' in window.history)) return;

	var $body = $('body'),
		$page = $('.current-page');

	function load(url) {
		console.log('Load', url);

    $('.unloading').remove();
		$page.addClass('unloading');

		$page = $('<div/>').appendTo($body);

		$page.load(url + ' main', function() {
			$(document).trigger('pageload', this.href);
			$body.scrollTop(0);
		});
	}

	$body.on('click', 'a', function(event){
		var href = this.getAttribute('href');
		if (href.indexOf('#') == 0 || href.indexOf(':') >= 0) return;

		href = this.href;

		console.log('pushState', href);
		window.history.pushState({push:true}, '', href);

		load(href);

		event.preventDefault();
	});

	window.onpopstate = function(event) {
		console.log('Popstate', location.href);
		if (event.state && event.state.push) {
			load(location.href);
		}
	};

}();

$('body').on('submit', 'form', function(){
	alert('Sistema de testes, desabilitei o submit do cartão, claro');
	return false;
});

// new Notification('Pagamento confirmado', {icon: 'img/icon.png', body: 'Saída liberada até ' + (new Date().getHours()+1) + 'h'})

if ('serviceWorker' in navigator && (window.location.protocol === 'https:' || window.location.hostname === 'localhost')) {
  navigator.serviceWorker.register('./service-worker.js', {
    scope: './shopping/'
  }).then(function(registration) {
    // Check to see if there's an updated version of service-worker.js with new files to cache:
    // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-registration-update-method
    if (typeof registration.update == 'function') {
      registration.update();
    }

    // updatefound is fired if service-worker.js changes.
    registration.onupdatefound = function() {
      // The updatefound event implies that registration.installing is set; see
      // https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#service-worker-container-updatefound-event
      var installingWorker = registration.installing;

      installingWorker.onstatechange = function() {
        switch (installingWorker.state) {
          case 'installed':
            if (navigator.serviceWorker.controller) {
              // At this point, the old content will have been purged and the fresh content will
              // have been added to the cache.
              // It's the perfect time to display a "New content is available; please refresh."
              // message in the page's interface.
              console.log('New or updated content is available.');
              location.reload();
            } else {
              // At this point, everything has been precached, but the service worker is not
              // controlling the page. The service worker will not take control until the next
              // reload or navigation to a page under the registered scope.
              // It's the perfect time to display a "Content is cached for offline use." message.
              console.log('Content is cached, and will be available for offline use the ' +
                          'next time the page is loaded.')
            }
          break;

          case 'redundant':
            console.error('The installing service worker became redundant.');
          break;
        }
      };
    };
  }).catch(function(e) {
    console.error('Error during service worker registration:', e);
  });
}<|endoftext|>


<commit_before>// @flow
/* eslint-disable no-undef */

export type Effect<Action, State> = {
  type: 'Call',
  args: any[],
  fn: (...args: any[]) => Promise<any>,
} | {
  type: 'All',
  ships: Generator<Effect<Action, State>, any, any>[],
} | {
  type: 'Next',
  action: Action,
} | {
  type: 'GetState',
};

export type t<Action, State, A> = Generator<Effect<Action, State>, A, any>;

function run<Action, State, A>(
  next: (action: Action) => void | Promise<void>,
  getState: () => State,
  ship: t<Action, State, A>,
  answer?: any)
  : Promise<A> {
  const result = ship.next(answer);
  if (result.done) {
    return Promise.resolve((result.value: any));
  }
  switch (result.value.type) {
  case 'Call': {
    const fnResult = result.value.fn(...result.value.args);
    return fnResult.then(newAnswer =>
      run(next, getState, ship, newAnswer)
    );
  }
  case 'All':
    return Promise.all(result.value.ships.map(currentShip =>
      run(next, getState, currentShip))
    ).then(newAnswer =>
      run(next, getState, ship, newAnswer)
    );
  case 'Next':
    return Promise.resolve(next(result.value.action)).then(() =>
      run(next, getState, ship)
    );
  case 'GetState': {
    const newAnswer = getState();
    return run(next, getState, ship, newAnswer);
  }
  default:
    return result.value;
  }
}

type ReduxStore<Action, State> = {
  dispatch: (action: Action) => void | Promise<void>,
  getState: () => State,
};

type ReduxMiddleware<Action, State> =
  (store: ReduxStore<Action, State>) =>
  (next: (action: Action) => void | Promise<void>) =>
  (action: Action) =>
  any;

export function middleware<Action, State>(
  actionToShip: (action: Action) => t<Action, State, void>
): ReduxMiddleware<Action, State> {
  return store => next => action => {
    const ship = actionToShip(action);
    run(next, store.getState, ship);
    return next(action);
  };
}

function* callAny<Action, State, A>(
  fn: (...args: any[]) => A | Promise<A>, ...args: any[]
): t<Action, State, A> {
  const result: any = yield {
    type: 'Call',
    args,
    fn: (...args) => Promise.resolve(fn(...args)),
  };
  return result;
}

export function call0<Action, State, A>(value: A | Promise<A>)
  : t<Action, State, A> {
  return callAny(() => value);
}

export const call1: <Action, State, A1, B>(
  fn: (arg1: A1) =>
    B | Promise<B>,
  arg1: A1
) => t<Action, State, B> =
  callAny;

export const call2: <Action, State, A1, A2, B>(
  fn: (arg1: A1, arg2: A2) =>
    B | Promise<B>,
  arg1: A1, arg2: A2
) => t<Action, State, B> =
  callAny;

export const call3: <Action, State, A1, A2, A3, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3
) => t<Action, State, B> =
  callAny;

export const call4: <Action, State, A1, A2, A3, A4, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4
) => t<Action, State, B> =
  callAny;

export const call5: <Action, State, A1, A2, A3, A4, A5, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5
) => t<Action, State, B> =
  callAny;

export const call6: <Action, State, A1, A2, A3, A4, A5, A6, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6
) => t<Action, State, B> =
  callAny;

export const call7: <Action, State, A1, A2, A3, A4, A5, A6, A7, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7
) => t<Action, State, B> =
  callAny;

function* allAny<Action, State>(...ships: t<Action, State, any>[]): t<Action, State, any[]> {
  const result: any = yield {
    type: 'All',
    ships,
  };
  return result;
}

export function all<Action, State, A>(ships: t<Action, State, A>[]): t<Action, State, A[]> {
  return allAny(...ships);
}

export const all2: <Action, State, A1, A2>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>
) => t<Action, State, [A1, A2]> =
  allAny;

export const all3: <Action, State, A1, A2, A3>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>
) => t<Action, State, [A1, A2, A3]> =
  allAny;

export const all4: <Action, State, A1, A2, A3, A4>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>
) => t<Action, State, [A1, A2, A3, A4]> =
  allAny;

export const all5: <Action, State, A1, A2, A3, A4, A5>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>,
  ship4: t<Action, State, A5>
) => t<Action, State, [A1, A2, A3, A4, A5]> =
  allAny;

export const all6: <Action, State, A1, A2, A3, A4, A5, A6>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>,
  ship4: t<Action, State, A5>,
  ship4: t<Action, State, A6>
) => t<Action, State, [A1, A2, A3, A4, A5, A6]> =
  allAny;

export const all7: <Action, State, A1, A2, A3, A4, A5, A6, A7>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>,
  ship4: t<Action, State, A5>,
  ship4: t<Action, State, A6>,
  ship4: t<Action, State, A7>
) => t<Action, State, [A1, A2, A3, A4, A5, A6, A7]> =
  allAny;

export function* next<Action, State>(action: Action): t<Action, State, void> {
  yield {
    type: 'Next',
    action,
  };
}

export function* getState<Action, State>(): t<Action, State, State> {
  const state: any = yield {
    type: 'GetState',
  };
  return state;
}

function* mapWithAnswer<Action1, State1, Action2, State2, A>(
  ship: t<Action1, State1, A>,
  mapAction: (action1: Action1) => Action2,
  mapState: (state2: State2) => State1,
  answer?: any
): t<Action2, State2, A> {
  const result = ship.next(answer);
  if (result.done) {
    return (result.value: any);
  }
  switch (result.value.type) {
  case 'Call': {
    const newAnswer = yield result.value;
    return yield* mapWithAnswer(ship, mapAction, mapState, newAnswer);
  }
  case 'All': {
    const newAnswer = yield {
      type: 'All',
      ships: result.value.ships.map((currentShip) =>
        mapWithAnswer(currentShip, mapAction, mapState)),
    };
    return yield* mapWithAnswer(ship, mapAction, mapState, newAnswer);
  }
  case 'Next':
    yield {
      type: 'Next',
      action: mapAction(result.value.action),
    };
    return yield* mapWithAnswer(ship, mapAction, mapState);
  case 'GetState': {
    const newAnswer: any = yield result.value;
    return yield* mapWithAnswer(ship, mapAction, mapState, mapState(newAnswer));
  }
  default:
    return result.value;
  }
}

export const map: <Action1, State1, Action2, State2, A>(
  ship: t<Action1, State1, A>,
  mapAction: (action1: Action1) => Action2,
  mapState: (state2: State2) => State1
) => t<Action2, State2, A> =
  mapWithAnswer;

export type Trace<Action, State> = {
  type: 'Done',
  result: any,
} | {
  type: 'Call',
  args: any[],
  next: Trace<Action, State>,
  result: any,
} | {
  type: 'All',
  next: Trace<Action, State>,
  traces: Trace<Action, State>[],
} | {
  type: 'Next',
  action: Action,
  next: Trace<Action, State>,
} | {
  type: 'GetState',
  next: Trace<Action, State>,
  state: State,
};

function* traceWithAnswer<Action, State, A>(ship: t<Action, State, A>, answer?: any)
  : t<Action, State, {result: A, trace: Trace<Action, State>}> {
  const result = ship.next(answer);
  if (result.done) {
    return {
      result: (result.value: any),
      trace: {
        type: 'Done',
        result: result.value,
      },
    };
  }
  switch (result.value.type) {
  case 'Call': {
    const {value} = result;
    const newAnswer: any = yield value;
    const next = yield* traceWithAnswer(ship, newAnswer);
    return {
      result: next.result,
      trace: {
        type: 'Call',
        args: value.args,
        next: next.trace,
        result: newAnswer,
      },
    };
  }
  case 'All': {
    const newAnswer: any = yield {
      type: 'All',
      ships: result.value.ships.map(traceWithAnswer),
    };
    const next = yield* traceWithAnswer(ship, newAnswer.map((currentAnswer) => currentAnswer.result));
    return {
      result: next.result,
      trace: {
        type: 'All',
        next: next.trace,
        traces: newAnswer.map((currentAnswer) => currentAnswer.trace),
      },
    };
  }
  case 'Next': {
    const {value} = result;
    yield value;
    const next = yield* traceWithAnswer(ship);
    return {
      result: next.result,
      trace: {
        type: 'Next',
        action: value.action,
        next: next.trace,
      },
    };
  }
  case 'GetState': {
    const newAnswer: any = yield result.value;
    const next = yield* traceWithAnswer(ship, newAnswer);
    return {
      result: next.result,
      trace: {
        type: 'GetState',
        next: next.trace,
        state: newAnswer,
      },
    };
  }
  default:
    return result.value;
  }
}

export const trace: <Action, State, A>(
  ship: t<Action, State, A>
) => t<Action, State, {result: A, trace: Trace<Action, State>}> =
  traceWithAnswer;

function delayPromise(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export function delay<Action, State>(ms: number): t<Action, State, void> {
  return call1(delayPromise, ms);
}
<commit_msg>Beginning of a simulation system<commit_after>// @flow
/* eslint-disable no-undef */

export type Effect<Action, State> = {
  type: 'Call',
  args: any[],
  fn: (...args: any[]) => Promise<any>,
} | {
  type: 'All',
  ships: Generator<Effect<Action, State>, any, any>[],
} | {
  type: 'Next',
  action: Action,
} | {
  type: 'GetState',
};

export type t<Action, State, A> = Generator<Effect<Action, State>, A, any>;

function runWithAnswer<Action, State, A>(
  next: (action: Action) => void | Promise<void>,
  getState: () => State,
  ship: t<Action, State, A>,
  answer?: any)
  : Promise<A> {
  const result = ship.next(answer);
  if (result.done) {
    return Promise.resolve((result.value: any));
  }
  switch (result.value.type) {
  case 'Call': {
    const fnResult = result.value.fn(...result.value.args);
    return fnResult.then(newAnswer =>
      runWithAnswer(next, getState, ship, newAnswer)
    );
  }
  case 'All':
    return Promise.all(result.value.ships.map(currentShip =>
      runWithAnswer(next, getState, currentShip))
    ).then(newAnswer =>
      runWithAnswer(next, getState, ship, newAnswer)
    );
  case 'Next':
    return Promise.resolve(next(result.value.action)).then(() =>
      runWithAnswer(next, getState, ship)
    );
  case 'GetState': {
    const newAnswer = getState();
    return runWithAnswer(next, getState, ship, newAnswer);
  }
  default:
    return result.value;
  }
}

type ReduxStore<Action, State> = {
  dispatch: (action: Action) => void | Promise<void>,
  getState: () => State,
};

type ReduxMiddleware<Action, State> =
  (store: ReduxStore<Action, State>) =>
  (next: (action: Action) => void | Promise<void>) =>
  (action: Action) =>
  any;

export function middleware<Action, State>(
  actionToShip: (action: Action) => t<Action, State, void>
): ReduxMiddleware<Action, State> {
  return store => next => action => {
    const ship = actionToShip(action);
    runWithAnswer(next, store.getState, ship);
    return next(action);
  };
}

function* callAny<Action, State, A>(
  fn: (...args: any[]) => A | Promise<A>, ...args: any[]
): t<Action, State, A> {
  const result: any = yield {
    type: 'Call',
    args,
    fn: (...args) => Promise.resolve(fn(...args)),
  };
  return result;
}

export function call0<Action, State, A>(value: A | Promise<A>)
  : t<Action, State, A> {
  return callAny(() => value);
}

export const call1: <Action, State, A1, B>(
  fn: (arg1: A1) =>
    B | Promise<B>,
  arg1: A1
) => t<Action, State, B> =
  callAny;

export const call2: <Action, State, A1, A2, B>(
  fn: (arg1: A1, arg2: A2) =>
    B | Promise<B>,
  arg1: A1, arg2: A2
) => t<Action, State, B> =
  callAny;

export const call3: <Action, State, A1, A2, A3, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3
) => t<Action, State, B> =
  callAny;

export const call4: <Action, State, A1, A2, A3, A4, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4
) => t<Action, State, B> =
  callAny;

export const call5: <Action, State, A1, A2, A3, A4, A5, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5
) => t<Action, State, B> =
  callAny;

export const call6: <Action, State, A1, A2, A3, A4, A5, A6, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6
) => t<Action, State, B> =
  callAny;

export const call7: <Action, State, A1, A2, A3, A4, A5, A6, A7, B>(
  fn: (arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7) =>
    B | Promise<B>,
  arg1: A1, arg2: A2, arg3: A3, arg4: A4, arg5: A5, arg6: A6, arg7: A7
) => t<Action, State, B> =
  callAny;

function* allAny<Action, State>(...ships: t<Action, State, any>[]): t<Action, State, any[]> {
  const result: any = yield {
    type: 'All',
    ships,
  };
  return result;
}

export function all<Action, State, A>(ships: t<Action, State, A>[]): t<Action, State, A[]> {
  return allAny(...ships);
}

export const all2: <Action, State, A1, A2>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>
) => t<Action, State, [A1, A2]> =
  allAny;

export const all3: <Action, State, A1, A2, A3>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>
) => t<Action, State, [A1, A2, A3]> =
  allAny;

export const all4: <Action, State, A1, A2, A3, A4>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>
) => t<Action, State, [A1, A2, A3, A4]> =
  allAny;

export const all5: <Action, State, A1, A2, A3, A4, A5>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>,
  ship4: t<Action, State, A5>
) => t<Action, State, [A1, A2, A3, A4, A5]> =
  allAny;

export const all6: <Action, State, A1, A2, A3, A4, A5, A6>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>,
  ship4: t<Action, State, A5>,
  ship4: t<Action, State, A6>
) => t<Action, State, [A1, A2, A3, A4, A5, A6]> =
  allAny;

export const all7: <Action, State, A1, A2, A3, A4, A5, A6, A7>(
  ship1: t<Action, State, A1>,
  ship2: t<Action, State, A2>,
  ship3: t<Action, State, A3>,
  ship4: t<Action, State, A4>,
  ship4: t<Action, State, A5>,
  ship4: t<Action, State, A6>,
  ship4: t<Action, State, A7>
) => t<Action, State, [A1, A2, A3, A4, A5, A6, A7]> =
  allAny;

export function* next<Action, State>(action: Action): t<Action, State, void> {
  yield {
    type: 'Next',
    action,
  };
}

export function* getState<Action, State>(): t<Action, State, State> {
  const state: any = yield {
    type: 'GetState',
  };
  return state;
}

function* mapWithAnswer<Action1, State1, Action2, State2, A>(
  ship: t<Action1, State1, A>,
  mapAction: (action1: Action1) => Action2,
  mapState: (state2: State2) => State1,
  answer?: any
): t<Action2, State2, A> {
  const result = ship.next(answer);
  if (result.done) {
    return (result.value: any);
  }
  switch (result.value.type) {
  case 'Call': {
    const newAnswer = yield result.value;
    return yield* mapWithAnswer(ship, mapAction, mapState, newAnswer);
  }
  case 'All': {
    const newAnswer = yield {
      type: 'All',
      ships: result.value.ships.map((currentShip) =>
        mapWithAnswer(currentShip, mapAction, mapState)),
    };
    return yield* mapWithAnswer(ship, mapAction, mapState, newAnswer);
  }
  case 'Next':
    yield {
      type: 'Next',
      action: mapAction(result.value.action),
    };
    return yield* mapWithAnswer(ship, mapAction, mapState);
  case 'GetState': {
    const newAnswer: any = yield result.value;
    return yield* mapWithAnswer(ship, mapAction, mapState, mapState(newAnswer));
  }
  default:
    return result.value;
  }
}

export const map: <Action1, State1, Action2, State2, A>(
  ship: t<Action1, State1, A>,
  mapAction: (action1: Action1) => Action2,
  mapState: (state2: State2) => State1
) => t<Action2, State2, A> =
  mapWithAnswer;

export type Trace<Action, State> = {
  type: 'Done',
  result?: any,
} | {
  type: 'Call',
  args: any[],
  next: Trace<Action, State>,
  result?: any,
} | {
  type: 'All',
  next: Trace<Action, State>,
  traces: Trace<Action, State>[],
} | {
  type: 'Next',
  action: Action,
  next: Trace<Action, State>,
} | {
  type: 'GetState',
  next: Trace<Action, State>,
  state: State,
};

function* traceWithAnswer<Action, State, A>(ship: t<Action, State, A>, answer?: any)
  : t<Action, State, {result: A, trace: Trace<Action, State>}> {
  const result = ship.next(answer);
  if (result.done) {
    return {
      result: (result.value: any),
      trace: {
        type: 'Done',
        result: result.value,
      },
    };
  }
  switch (result.value.type) {
  case 'Call': {
    const {value} = result;
    const newAnswer: any = yield value;
    const next = yield* traceWithAnswer(ship, newAnswer);
    return {
      result: next.result,
      trace: {
        type: 'Call',
        args: value.args,
        next: next.trace,
        result: newAnswer,
      },
    };
  }
  case 'All': {
    const newAnswer: any = yield {
      type: 'All',
      ships: result.value.ships.map(traceWithAnswer),
    };
    const next = yield* traceWithAnswer(ship, newAnswer.map((currentAnswer) => currentAnswer.result));
    return {
      result: next.result,
      trace: {
        type: 'All',
        next: next.trace,
        traces: newAnswer.map((currentAnswer) => currentAnswer.trace),
      },
    };
  }
  case 'Next': {
    const {value} = result;
    yield value;
    const next = yield* traceWithAnswer(ship);
    return {
      result: next.result,
      trace: {
        type: 'Next',
        action: value.action,
        next: next.trace,
      },
    };
  }
  case 'GetState': {
    const newAnswer: any = yield result.value;
    const next = yield* traceWithAnswer(ship, newAnswer);
    return {
      result: next.result,
      trace: {
        type: 'GetState',
        next: next.trace,
        state: newAnswer,
      },
    };
  }
  default:
    return result.value;
  }
}

export const trace: <Action, State, A>(
  ship: t<Action, State, A>
) => t<Action, State, {result: A, trace: Trace<Action, State>}> =
  traceWithAnswer;

function simulationError<Action, State>(actual: string, expected: string): Trace<Action, State> {
  return {
    type: 'Done',
    result: {
      actual,
      expected,
      message: 'Simulation error',
    },
  };
}

function simulateWithAnswer<Action, State, A>(
  ship: t<Action, State, A>,
  trace: Trace<Action, State>,
  answer?: any
): Trace<Action, State> {
  const result = ship.next(answer);
  if (result.done) {
    return {
      type: 'Done',
      result: result.value,
    };
  }
  switch (result.value.type) {
  case 'Call': {
    if (trace.type === 'Call') {
      const newAnswer = trace.result;
      return {
        type: 'Call',
        args: result.value.args,
        next: simulateWithAnswer(ship, trace.next, newAnswer),
        result: newAnswer,
      };
    }
    return simulationError(trace.type, 'Call');
  }
  case 'All': {
    if (trace.type === 'All') {
      const {traces} = trace;
      const newAnswer = result.value.ships.map((ship, shipIndex) =>
        simulateWithAnswer(ship, traces[shipIndex])
      );
      return {
        type: 'All',
        next: simulateWithAnswer(ship, trace.next, newAnswer),
        traces: newAnswer,
      };
    }
    return simulationError(trace.type, 'All');
  }
  case 'Next': {
    if (trace.type === 'Next') {
      return {
        type: 'Next',
        action: result.value.action,
        next: simulateWithAnswer(ship, trace.next),
      };
    }
    return simulationError(trace.type, 'Next');
  }
  case 'GetState': {
    if (trace.type === 'GetState') {
      const newAnswer = trace.state;
      return {
        type: 'GetState',
        next: simulateWithAnswer(ship, trace.next, newAnswer),
        state: newAnswer,
      };
    }
    return simulationError(trace.type, 'GetState');
  }
  default:
    return result.value;
  }
}

export const simulate: <Action, State, A>(
  ship: t<Action, State, A>,
  trace: Trace<Action, State>
) => Trace<Action, State> =
  simulateWithAnswer;

function delayPromise(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

export function delay<Action, State>(ms: number): t<Action, State, void> {
  return call1(delayPromise, ms);
}
<|endoftext|>


<commit_before>import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import reducer from './reducers';

const configureStore = () => {
  const middlewares = [thunk];
  if (process.env.NODE_ENV === 'development') {
    const logger = createLogger();
    middlewares.push(logger);
  }

  return createStore(
    reducer,
    applyMiddleware(...middlewares)
  );
};

export default configureStore;
<commit_msg>log when not in production<commit_after>import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import reducer from './reducers';

const configureStore = () => {
  const middlewares = [thunk];
  if (process.env.NODE_ENV !== 'production') {
    const logger = createLogger();
    middlewares.push(logger);
  }

  return createStore(
    reducer,
    applyMiddleware(...middlewares)
  );
};

export default configureStore;
<|endoftext|>