<commit_before>/**
 * The CountriesView selector view.
 *
 * @return CountriesView instance (extends Backbone.View).
 */
define([
  'underscore',
  'handlebars',
  'amplify',
  'chosen',
  'map/presenters/tabs/CountriesPresenter',
  'text!map/templates/tabs/countries.handlebars',
  'text!map/templates/tabs/countriesIso.handlebars',
  'text!map/templates/tabs/countriesButtons.handlebars',
  'text!map/templates/tabs/countries-mobile.handlebars'
], function(_, Handlebars, amplify, chosen, Presenter, tpl, tplIso, tplButtons, tplMobile) {

  'use strict';

  var CountriesModel = Backbone.Model.extend({
    defaults: {
      country_layers: null
    }
  });

  var CountriesView = Backbone.View.extend({

    el: '#countries-tab',

    template: Handlebars.compile(tpl),
    templateIso: Handlebars.compile(tplIso),
    templateButtons: Handlebars.compile(tplButtons),
    templateMobile: Handlebars.compile(tplMobile),

    events: {
      //countries
      'click .layer': 'toggleLayer',
      'click .wrapped-layer': 'toggleLayerWrap',

      'click #countries-analyze-button' : 'analyzeIso',
      'change #countries-country-select' : 'changeIso',

      'click #countries-country-ul li' : 'changeIsoMobile',
      'click #countries-country-reset' : 'changeIsoMobile',

      'click #countries-letters-ul li' : 'setLetter'

    },

    initialize: function(map) {
      this.embed = $('body').hasClass('is-embed-action');
      this.map = map;
      this.model = new CountriesModel();
      this.presenter = new Presenter(this);
      enquire.register("screen and (min-width:"+window.gfw.config.GFW_MOBILE+"px)", {
        match: _.bind(function(){
          this.mobile = false;
          this.render();
        },this)
      });
      enquire.register("screen and (max-width:"+window.gfw.config.GFW_MOBILE+"px)", {
        match: _.bind(function(){
          this.mobile = true;
          this.renderMobile();
        },this)
      });

      //Experiment
      //this.presenter.initExperiment('source');
    },

    render: function(){
      this.$el.html(this.template());
      this.cacheVars();
    },
    renderMobile: function(){
      this.$el.html(this.templateMobile());
      this.cacheVars();
    },

    cacheVars: function(){

      //toggle-countries-content
      this.$toggle = $('#toggle-countries-content');
      //buttons
      this.$buttons = $('#countries-buttons');
      //layers
      this.$layers = $('#countries-layers');

      //country
      this.$selects = this.$el.find('.chosen-select');
      this.$countrySelect = $('#countries-country-select');
      this.$countryUl = $('#countries-country-ul');
      this.$countryReset = $('#countries-country-reset');
      this.$countryBack = $('#country-tab-mobile-btn-back');
      this.$countryName = $('#countries-name');
      this.$letters = $('#countries-letters-ul');
      this.inits();
    },

    inits: function(){
      // countries
      this.setStyle(0.45);
      this.getCountries();
      this.$countryReset.on('click', _.bind(this.changeIsoMobile, this));
    },

    /**
     * Set geojson style.
     */
    setStyle: function(opacity) {
      this.style = {
        strokeWeight: 2,
        fillOpacity: opacity,
        fillColor: '#FFF',
        strokeColor: '#A2BC28',
        icon: new google.maps.MarkerImage(
          '/assets/icons/marker_exclamation.png',
          new google.maps.Size(36, 36), // size
          new google.maps.Point(0, 0), // offset
          new google.maps.Point(18, 18) // anchor
        )
      };

      this.map.data.setStyle(this.style);
    },

    getIsoLayers: function(layers){
      this.isoLayers = layers;
    },

    setIsoLayers: function(){
      var layersToRender = [];
      _.each(this.isoLayers, _.bind(function(layer){
        if (layer.iso === this.iso) {
          layersToRender.push(layer);
        }
      }, this ));
      this.renderIsoLayer(layersToRender);
    },

    renderIsoLayer: function(layersToRender){
      this.$layers.html(this.templateIso({ layers: layersToRender }));
      this._renderHtml();
      this._selectSubIsoLayer();
    },

    _renderHtml: function(){
      this.$layers.find('.layers-list').html($('#country-layers .layers-list').html())
    },

    _selectSubIsoLayer: function() {
      var parentSelected = this.$layers.find('.layer:first').hasClass('selected');
      var subLayersSelected = this.$layers.find('.wrapped.selected').length > 0;
      if (!subLayersSelected && parentSelected) {
        this.$layers.find('.wrapped:first').click();
      }
    },

    toggleLayer: function(event) {
      // event.stopPropagation();
      event.preventDefault();

      if (!$(event.target).hasClass('source') && !$(event.target).parent().hasClass('source')) {
        var $li = $(event.currentTarget);
        var layerSlug = $li.data('layer');
        var layer = _.where(this.isoLayers, {slug: layerSlug})[0];

        if (layer) {
          $('#country-layers [data-layer="'+layerSlug+'"]:first').click()
          ga('send', 'event', 'Map', 'Toggle', 'Layer: ' + layerSlug);
        }
      }
    },

    toggleLayerWrap: function(e){
      if (!$(e.target).hasClass('source') && !$(e.target).parent().hasClass('source') && !$(e.target).hasClass('layer') && !$(e.target).parents('.wrapped').hasClass('layer')) {
        var $li = $(e.currentTarget);
        var layerSlug = $li.data('layer');
        $('#country-layers [data-layer="'+layerSlug+'"]:first').click();
      }
    },

    setButtons: function(to, country){
      this.$toggle.toggleClass('active', to);
      var isSpecialAtlas = (!!country && !!country.iso && country.iso == 'IDN') || false;
      this.$buttons.html(this.templateButtons( {iso: this.iso, country: country, isSpecialAtlas: isSpecialAtlas} ));
    },

    analyzeIso: function(e){
      this.presenter.setAnalyze(null);
    },

    /**
     * COUNTRY
     */
    getCountries: function(){
      if (!amplify.store('countries')) {
        var sql = ['SELECT c.iso, c.name FROM gfw2_countries c WHERE c.enabled = true'];
        $.ajax({
          url: 'https://wri-01.cartodb.com/api/v2/sql?q='+sql,
          dataType: 'json',
          success: _.bind(function(data){
            amplify.store('countries', data.rows);
            this.printCountries();
          }, this ),
          error: function(error){
            console.log(error);
          }
        });
      }else{
        this.printCountries()
      }
    },

    /**
     * Print countries.
     */
    printCountries: function(){
      //Country select
      this.countries = amplify.store('countries');

      if(this.mobile){
        var options = "";
        var letters = [];
        _.each(_.sortBy(this.countries, function(country){ return country.name }), _.bind(function(country, i){
          var letter = country.name.charAt(0).toUpperCase();
          options += '
'+ country.name + '
';
          letters.push(letter);
        }, this ));
        letters = _.uniq(letters);
        this.$countryUl.html(options);
        this.setLettersVisibility(letters);

      } else {
        //Loop for print options
        var options = "<option></option>";
        _.each(_.sortBy(this.countries, function(country){ return country.name }), _.bind(function(country, i){
          options += '<option value="'+ country.iso +'">'+ country.name + '</option>';
        }, this ));
        this.$countrySelect.append(options);
        this.$selects.chosen({
          width: '100%',
          allow_single_deselect: true,
          inherit_select_classes: true,
          no_results_text: "Oops, nothing found!"
        });
      }
    },

    // Please refactor
    setLettersVisibility: function(arr){
      _.each(this.$letters.find('li'), _.bind(function(v){
        var alpha = $(v).data('alpha');
        if (arr.indexOf(alpha) == -1) {
          $(v).addClass('disabled');
        }
      }, this ))

    },

    setLetter: function(e){
      var current = $(e.currentTarget), filter;
      if (!current.hasClass('disabled')) {
        if (current.hasClass('current')) {
          this.$letters.find('li').removeClass('current');
          filter = null;
        }else{
          this.$letters.find('li').removeClass('current');
          current.addClass('current');
          filter = current.data('alpha');
        }
        this.filterByLetter(filter)
      }

    },

    filterByLetter: function(filter){
      this.$countryUl.find('li').filter(function(k,v){
        if (!!filter) {
          ($(v).data('alpha') == filter) ? $(v).removeClass('hidden') : $(v).addClass('hidden');
        }else{
          $(v).removeClass('current hidden');
        }
      });
      (! !!filter) ? this.$letters.find('li').removeClass('current') : null;
    },

    // Select change iso
    changeIso: function(e){
      this.iso = $(e.currentTarget).val() || null;
      this.commonIsoChanges();
      this.presenter.changeIso({country: this.iso, region: null});
    },

    changeIsoMobile: function(e){
      this.iso = $(e.currentTarget).data('value') || null;
      this.commonIsoChanges();
      this.presenter.changeIso({country: this.iso, region: null});
    },

    // For autoselect country and region when youn reload page
    setSelects: function(iso){
      this.iso = iso.country;
      this.commonIsoChanges();
      this.$countrySelect.val(this.iso).trigger("liszt:updated");
      if (this.mobile) {
        this.filterByLetter(null);
      }
    },

    commonIsoChanges: function(){
      this.setIsoLayers();
      this.setButtons(!!this.iso);
      if (this.mobile) {
        var country = _.find(amplify.store('countries'), _.bind(function(country){
          return country.iso === this.iso;
        }, this ));
        var name = (country) ? country.name + ' Data' : 'Country Data';
        var shareName = (country) ? country.name : '';
        this.$countryName.text(name);
        this.presenter.changeNameIso(shareName);
        if(!!this.iso) {
          this.$countryReset.show(0);
          this.$countryBack.hide(0);
          this.$countryUl.addClass('hidden');
          this.$letters.parents('.letters-ul-mobile-box').addClass('hidden');
          this.filterByLetter(null);
        }else{
          this.$countryReset.hide(0);
          this.$countryBack.show(0);
          this.$countryUl.removeClass('hidden');
          this.$letters.parents('.letters-ul-mobile-box').removeClass('hidden');
          this.filterByLetter(null);
        }
      };
      this.$countrySelect.val(this.iso).trigger("liszt:updated");
      // this.$regionSelect.val(this.area).trigger("liszt:updated");
      if (this.iso) {
        this.getAdditionalInfoCountry();
      }
    },

    getAdditionalInfoCountry: function(){
      if (!amplify.store('country-'+this.iso)) {
        $.ajax({
          url: window.gfw.config.GFW_API_HOST + '/countries/'+this.iso,
          dataType: 'json',
          success: _.bind(function(data){
            amplify.store('country-'+this.iso, data);
            this.setAdditionalInfoCountry();
          }, this ),
          error: function(error){
            console.log(error);
            mps.publish('Spinner/stop')
          }
        });
      }else{
        this.setAdditionalInfoCountry()
      }
    },

    setAdditionalInfoCountry: function(){
      var country = amplify.store('country-'+this.iso);
      this.setButtons(!!this.iso, country);
    },

  });

  return CountriesView;

});
<commit_msg>Map: country layers do not select automatically any layer<commit_after>/**
 * The CountriesView selector view.
 *
 * @return CountriesView instance (extends Backbone.View).
 */
define([
  'underscore',
  'handlebars',
  'amplify',
  'chosen',
  'map/presenters/tabs/CountriesPresenter',
  'text!map/templates/tabs/countries.handlebars',
  'text!map/templates/tabs/countriesIso.handlebars',
  'text!map/templates/tabs/countriesButtons.handlebars',
  'text!map/templates/tabs/countries-mobile.handlebars'
], function(_, Handlebars, amplify, chosen, Presenter, tpl, tplIso, tplButtons, tplMobile) {

  'use strict';

  var CountriesModel = Backbone.Model.extend({
    defaults: {
      country_layers: null
    }
  });

  var CountriesView = Backbone.View.extend({

    el: '#countries-tab',

    template: Handlebars.compile(tpl),
    templateIso: Handlebars.compile(tplIso),
    templateButtons: Handlebars.compile(tplButtons),
    templateMobile: Handlebars.compile(tplMobile),

    events: {
      //countries
      'click .layer': 'toggleLayer',
      'click .wrapped-layer': 'toggleLayerWrap',

      'click #countries-analyze-button' : 'analyzeIso',
      'change #countries-country-select' : 'changeIso',

      'click #countries-country-ul li' : 'changeIsoMobile',
      'click #countries-country-reset' : 'changeIsoMobile',

      'click #countries-letters-ul li' : 'setLetter'

    },

    initialize: function(map) {
      this.embed = $('body').hasClass('is-embed-action');
      this.map = map;
      this.model = new CountriesModel();
      this.presenter = new Presenter(this);
      enquire.register("screen and (min-width:"+window.gfw.config.GFW_MOBILE+"px)", {
        match: _.bind(function(){
          this.mobile = false;
          this.render();
        },this)
      });
      enquire.register("screen and (max-width:"+window.gfw.config.GFW_MOBILE+"px)", {
        match: _.bind(function(){
          this.mobile = true;
          this.renderMobile();
        },this)
      });

      //Experiment
      //this.presenter.initExperiment('source');
    },

    render: function(){
      this.$el.html(this.template());
      this.cacheVars();
    },
    renderMobile: function(){
      this.$el.html(this.templateMobile());
      this.cacheVars();
    },

    cacheVars: function(){

      //toggle-countries-content
      this.$toggle = $('#toggle-countries-content');
      //buttons
      this.$buttons = $('#countries-buttons');
      //layers
      this.$layers = $('#countries-layers');

      //country
      this.$selects = this.$el.find('.chosen-select');
      this.$countrySelect = $('#countries-country-select');
      this.$countryUl = $('#countries-country-ul');
      this.$countryReset = $('#countries-country-reset');
      this.$countryBack = $('#country-tab-mobile-btn-back');
      this.$countryName = $('#countries-name');
      this.$letters = $('#countries-letters-ul');
      this.inits();
    },

    inits: function(){
      // countries
      this.setStyle(0.45);
      this.getCountries();
      this.$countryReset.on('click', _.bind(this.changeIsoMobile, this));
    },

    /**
     * Set geojson style.
     */
    setStyle: function(opacity) {
      this.style = {
        strokeWeight: 2,
        fillOpacity: opacity,
        fillColor: '#FFF',
        strokeColor: '#A2BC28',
        icon: new google.maps.MarkerImage(
          '/assets/icons/marker_exclamation.png',
          new google.maps.Size(36, 36), // size
          new google.maps.Point(0, 0), // offset
          new google.maps.Point(18, 18) // anchor
        )
      };

      this.map.data.setStyle(this.style);
    },

    getIsoLayers: function(layers){
      this.isoLayers = layers;
    },

    setIsoLayers: function(){
      var layersToRender = [];
      _.each(this.isoLayers, _.bind(function(layer){
        if (layer.iso === this.iso) {
          layersToRender.push(layer);
        }
      }, this ));
      this.renderIsoLayer(layersToRender);
    },

    renderIsoLayer: function(layersToRender){
      this.$layers.html(this.templateIso({ layers: layersToRender }));
      this._renderHtml();
    },

    _renderHtml: function(){
      this.$layers.find('.layers-list').html($('#country-layers .layers-list').html())
    },

    toggleLayer: function(event) {
      // event.stopPropagation();
      event.preventDefault();

      if (!$(event.target).hasClass('source') && !$(event.target).parent().hasClass('source')) {
        var $li = $(event.currentTarget);
        var layerSlug = $li.data('layer');
        var layer = _.where(this.isoLayers, {slug: layerSlug})[0];

        if (layer) {
          $('#country-layers [data-layer="'+layerSlug+'"]:first').click()
          ga('send', 'event', 'Map', 'Toggle', 'Layer: ' + layerSlug);
        }
      }
    },

    toggleLayerWrap: function(e){
      if (!$(e.target).hasClass('source') && !$(e.target).parent().hasClass('source') && !$(e.target).hasClass('layer') && !$(e.target).parents('.wrapped').hasClass('layer')) {
        var $li = $(e.currentTarget);
        var layerSlug = $li.data('layer');
        $('#country-layers [data-layer="'+layerSlug+'"]:first').click();
      }
    },

    setButtons: function(to, country){
      this.$toggle.toggleClass('active', to);
      var isSpecialAtlas = (!!country && !!country.iso && country.iso == 'IDN') || false;
      this.$buttons.html(this.templateButtons( {iso: this.iso, country: country, isSpecialAtlas: isSpecialAtlas} ));
    },

    analyzeIso: function(e){
      this.presenter.setAnalyze(null);
    },

    /**
     * COUNTRY
     */
    getCountries: function(){
      if (!amplify.store('countries')) {
        var sql = ['SELECT c.iso, c.name FROM gfw2_countries c WHERE c.enabled = true'];
        $.ajax({
          url: 'https://wri-01.cartodb.com/api/v2/sql?q='+sql,
          dataType: 'json',
          success: _.bind(function(data){
            amplify.store('countries', data.rows);
            this.printCountries();
          }, this ),
          error: function(error){
            console.log(error);
          }
        });
      }else{
        this.printCountries()
      }
    },

    /**
     * Print countries.
     */
    printCountries: function(){
      //Country select
      this.countries = amplify.store('countries');

      if(this.mobile){
        var options = "";
        var letters = [];
        _.each(_.sortBy(this.countries, function(country){ return country.name }), _.bind(function(country, i){
          var letter = country.name.charAt(0).toUpperCase();
          options += '
'+ country.name + '
';
          letters.push(letter);
        }, this ));
        letters = _.uniq(letters);
        this.$countryUl.html(options);
        this.setLettersVisibility(letters);

      } else {
        //Loop for print options
        var options = "<option></option>";
        _.each(_.sortBy(this.countries, function(country){ return country.name }), _.bind(function(country, i){
          options += '<option value="'+ country.iso +'">'+ country.name + '</option>';
        }, this ));
        this.$countrySelect.append(options);
        this.$selects.chosen({
          width: '100%',
          allow_single_deselect: true,
          inherit_select_classes: true,
          no_results_text: "Oops, nothing found!"
        });
      }
    },

    // Please refactor
    setLettersVisibility: function(arr){
      _.each(this.$letters.find('li'), _.bind(function(v){
        var alpha = $(v).data('alpha');
        if (arr.indexOf(alpha) == -1) {
          $(v).addClass('disabled');
        }
      }, this ))

    },

    setLetter: function(e){
      var current = $(e.currentTarget), filter;
      if (!current.hasClass('disabled')) {
        if (current.hasClass('current')) {
          this.$letters.find('li').removeClass('current');
          filter = null;
        }else{
          this.$letters.find('li').removeClass('current');
          current.addClass('current');
          filter = current.data('alpha');
        }
        this.filterByLetter(filter)
      }

    },

    filterByLetter: function(filter){
      this.$countryUl.find('li').filter(function(k,v){
        if (!!filter) {
          ($(v).data('alpha') == filter) ? $(v).removeClass('hidden') : $(v).addClass('hidden');
        }else{
          $(v).removeClass('current hidden');
        }
      });
      (! !!filter) ? this.$letters.find('li').removeClass('current') : null;
    },

    // Select change iso
    changeIso: function(e){
      this.iso = $(e.currentTarget).val() || null;
      this.commonIsoChanges();
      this.presenter.changeIso({country: this.iso, region: null});
    },

    changeIsoMobile: function(e){
      this.iso = $(e.currentTarget).data('value') || null;
      this.commonIsoChanges();
      this.presenter.changeIso({country: this.iso, region: null});
    },

    // For autoselect country and region when youn reload page
    setSelects: function(iso){
      this.iso = iso.country;
      this.commonIsoChanges();
      this.$countrySelect.val(this.iso).trigger("liszt:updated");
      if (this.mobile) {
        this.filterByLetter(null);
      }
    },

    commonIsoChanges: function(){
      this.setIsoLayers();
      this.setButtons(!!this.iso);
      if (this.mobile) {
        var country = _.find(amplify.store('countries'), _.bind(function(country){
          return country.iso === this.iso;
        }, this ));
        var name = (country) ? country.name + ' Data' : 'Country Data';
        var shareName = (country) ? country.name : '';
        this.$countryName.text(name);
        this.presenter.changeNameIso(shareName);
        if(!!this.iso) {
          this.$countryReset.show(0);
          this.$countryBack.hide(0);
          this.$countryUl.addClass('hidden');
          this.$letters.parents('.letters-ul-mobile-box').addClass('hidden');
          this.filterByLetter(null);
        }else{
          this.$countryReset.hide(0);
          this.$countryBack.show(0);
          this.$countryUl.removeClass('hidden');
          this.$letters.parents('.letters-ul-mobile-box').removeClass('hidden');
          this.filterByLetter(null);
        }
      };
      this.$countrySelect.val(this.iso).trigger("liszt:updated");
      // this.$regionSelect.val(this.area).trigger("liszt:updated");
      if (this.iso) {
        this.getAdditionalInfoCountry();
      }
    },

    getAdditionalInfoCountry: function(){
      if (!amplify.store('country-'+this.iso)) {
        $.ajax({
          url: window.gfw.config.GFW_API_HOST + '/countries/'+this.iso,
          dataType: 'json',
          success: _.bind(function(data){
            amplify.store('country-'+this.iso, data);
            this.setAdditionalInfoCountry();
          }, this ),
          error: function(error){
            console.log(error);
            mps.publish('Spinner/stop')
          }
        });
      }else{
        this.setAdditionalInfoCountry()
      }
    },

    setAdditionalInfoCountry: function(){
      var country = amplify.store('country-'+this.iso);
      this.setButtons(!!this.iso, country);
    },

  });

  return CountriesView;

});
<|endoftext|>


<commit_before>import {accessor, applyDecorators} from 'index';
describe('accessor', () => {
  test('@accessor must accept a getter or setter', () => {
    expect(() => class {
      @accessor()
      foo () {}
    }).toThrow("@accessor need a getter or setter. If you don't need to add setter/getter. You should remove @accessor");
  });
  test('@accessor can change a data property into accessor property', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn(value);
      return value;
    };
    const set = get;
    class Foo {
      @accessor({get, set})
      bar = 1;
    }
    const foo = new Foo();
    expect(foo.bar).toBe(1);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(fn).lastCalledWith(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(2);
    expect(fn).lastCalledWith(2);
  });
  test('@accessor called function with instance', () => {
    const get = function (value) {
      expect(this).toBe(foo);
      return value;
    };
    const set = get;
    class Foo {
      @accessor({get, set})
      bar = 1;
    }
    const foo = new Foo();
    foo.bar = 2;
  });
  test('@accessor can handle function property', () => {
    const run = jest.fn();
    const operate = jest.fn();
    const get = function (value) {
      operate();
      return function () {
        run();
        value();
      };
    };
    const set = get;
    class Foo {
      @accessor({get, set})
      bar () {}
    }
    const foo = new Foo();
    const bar = foo.bar;
    expect(run).toHaveBeenCalledTimes(0);
    expect(operate).toHaveBeenCalledTimes(1);
    bar();
    expect(run).toHaveBeenCalledTimes(1);
    expect(operate).toHaveBeenCalledTimes(1);
    foo.bar = function () {};
    expect(run).toHaveBeenCalledTimes(1);
    expect(operate).toHaveBeenCalledTimes(2);
    foo.bar();
    expect(run).toHaveBeenCalledTimes(3);
    expect(operate).toHaveBeenCalledTimes(3);
  });
  test('@accessor can also handle getter and setter well', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn();
      return value;
    };
    const set = get;
    let value = 1;
    class Foo {
      @accessor({get, set})
      get bar () {
        return value;
      }
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(2);
  });
  test('@accessor can also handle getter and setter well even you just pass a get', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn();
      return value;
    };
    let value = 1;
    class Foo {
      @accessor({get})
      get bar () {
        return value;
      }
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(2);
  });
  test('@accessor can also handle getter and setter well even you just pass a set', () => {
    const fn = jest.fn();
    const set = function (value) {
      fn();
      return value;
    };
    let value = 1;
    class Foo {
      @accessor({set})
      get bar () {
        return value;
      }
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(0);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(2);
  });
  test('@accessor will warn if you pass it a setter function and offer it a accessor without setter, but it still do a good job', () => {
    const fn = jest.fn();
    const originConsole = console;
    global.console = Object.assign({}, originConsole, {warn: jest.fn()});
    const get = function (value) {
      fn();
      return value;
    };
    const set = get;
    const value = 1;
    class Foo {
      @accessor({get, set})
      get bar () {
        return value;
      }
    };
    const foo = new Foo();
    expect(console.warn).toHaveBeenCalledTimes(1);
    expect(console.warn).lastCalledWith("You are trying to set setter via @accessor on one property without setter. That's not a good idea.");
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(() => {foo.bar = 2;}).not.toThrow();
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(1);
    global.console = originConsole;
  });
  test('@accessor will warn if you pass it a getter function and offer it a accessor without getter, but it still do a good job', () => {
    const fn = jest.fn();
    const originConsole = console;
    global.console = Object.assign({}, originConsole, {warn: jest.fn()});
    const get = function (value) {
      fn();
      return value;
    };
    const set = get;
    let value = 1;
    class Foo {
      @accessor({get, set})
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(console.warn).toHaveBeenCalledTimes(1);
    expect(console.warn).lastCalledWith("You are trying to set getter via @accessor on one property without getter. That's not a good idea.");
    expect(foo.bar).toBe(undefined);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(() => {foo.bar = 2;}).not.toThrow();
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(undefined);
    expect(value).toBe(2);
    global.console = originConsole;
  });
  test('@accessor can just modify descriptor with only getter as you offet get', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn();
      return value;
    };
    const value = 1;
    class Foo {
      @accessor({get})
      get bar () {
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(() => {foo.bar = 2;}).toThrow('Cannot set property bar of #<Foo> which has only a getter');
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(1);
  });
  test('@accessor can just modify descriptor with only setter as you offet set', () => {
    const fn = jest.fn();
    const set = function (value) {
      fn();
      return value;
    };
    let value = 1;
    class Foo {
      @accessor({set})
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(undefined);
    expect(fn).toHaveBeenCalledTimes(0);
    expect(() => {foo.bar = 2;}).not.toThrow();
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(undefined);
    expect(value).toBe(2);
  });
  test('@accessor alse accept array of funtion as getter/setter', () => {
    const fn = jest.fn();
    const handler = function (value) {
      fn(value);
      expect(this).toBe(foo);
      return value;
    };
    const get = [handler, handler];
    const set = get;
    class Foo {
      @accessor({get, set})
      bar = 1;
    }
    const foo = new Foo();
    expect(foo.bar).toBe(1);
    expect(fn).toHaveBeenCalledTimes(2);
    expect(fn).lastCalledWith(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(4);
    expect(fn).lastCalledWith(2);
    expect(foo.bar).toBe(2);
  });
  test('@accessor will throw error when they accept illgal array', () => {
    expect(() => class {
      @accessor({get: []})
      bar = 1;
    }).toThrow("@accessor need a getter or setter. If you don't need to add setter/getter. You should remove @accessor");
    expect(() => class {
      @accessor({set: []})
      bar = 1;
    }).toThrow("@accessor need a getter or setter. If you don't need to add setter/getter. You should remove @accessor");
    expect(() => class {
      @accessor({get: [1]})
      bar = 1;
    }).toThrow('@accessor only accept function or array of function as getter/setter');
    expect(() => class {
      @accessor({get: [() => {}, 1]})
      bar = 1;
    }).toThrow('@accessor only accept function or array of function as getter/setter');
  });
  test('@accessor accept one function in the array, though we do not courage people to do this', () => {
    const fn = jest.fn();
    const handler = function (value) {
      fn(value);
      expect(this).toBe(foo);
      return value;
    };
    class Foo {
      @accessor({get: [handler], set: [handler]})
      bar = 1;
    }
    const foo = new Foo();
    expect(foo.bar).toBe(1);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(fn).lastCalledWith(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(2);
    expect(fn).lastCalledWith(2);
  });
  test('@accessor can accessor undefined', () => {
    let value = 1;
    class Foo {};
    applyDecorators(Foo, {
      a: accessor({
        get () {return value},
        set (val) {
          value = val;
          return value;
        }
      })
    });
    const foo = new Foo();
    expect(foo.a).toBe(value);
    foo.a = 2;
    expect(foo.a).toBe(value);
    expect(value).toBe(2);
  });
});
<commit_msg>[update] eslint fix what: why: how:<commit_after>import {accessor, applyDecorators} from 'index';
describe('accessor', () => {
  test('@accessor must accept a getter or setter', () => {
    expect(() => class {
      @accessor()
      foo () {}
    }).toThrow("@accessor need a getter or setter. If you don't need to add setter/getter. You should remove @accessor");
  });
  test('@accessor can change a data property into accessor property', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn(value);
      return value;
    };
    const set = get;
    class Foo {
      @accessor({get, set})
      bar = 1;
    }
    const foo = new Foo();
    expect(foo.bar).toBe(1);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(fn).lastCalledWith(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(2);
    expect(fn).lastCalledWith(2);
  });
  test('@accessor called function with instance', () => {
    const get = function (value) {
      expect(this).toBe(foo);
      return value;
    };
    const set = get;
    class Foo {
      @accessor({get, set})
      bar = 1;
    }
    const foo = new Foo();
    foo.bar = 2;
  });
  test('@accessor can handle function property', () => {
    const run = jest.fn();
    const operate = jest.fn();
    const get = function (value) {
      operate();
      return function () {
        run();
        value();
      };
    };
    const set = get;
    class Foo {
      @accessor({get, set})
      bar () {}
    }
    const foo = new Foo();
    const bar = foo.bar;
    expect(run).toHaveBeenCalledTimes(0);
    expect(operate).toHaveBeenCalledTimes(1);
    bar();
    expect(run).toHaveBeenCalledTimes(1);
    expect(operate).toHaveBeenCalledTimes(1);
    foo.bar = function () {};
    expect(run).toHaveBeenCalledTimes(1);
    expect(operate).toHaveBeenCalledTimes(2);
    foo.bar();
    expect(run).toHaveBeenCalledTimes(3);
    expect(operate).toHaveBeenCalledTimes(3);
  });
  test('@accessor can also handle getter and setter well', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn();
      return value;
    };
    const set = get;
    let value = 1;
    class Foo {
      @accessor({get, set})
      get bar () {
        return value;
      }
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(2);
  });
  test('@accessor can also handle getter and setter well even you just pass a get', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn();
      return value;
    };
    let value = 1;
    class Foo {
      @accessor({get})
      get bar () {
        return value;
      }
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(2);
  });
  test('@accessor can also handle getter and setter well even you just pass a set', () => {
    const fn = jest.fn();
    const set = function (value) {
      fn();
      return value;
    };
    let value = 1;
    class Foo {
      @accessor({set})
      get bar () {
        return value;
      }
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(0);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(2);
  });
  test('@accessor will warn if you pass it a setter function and offer it a accessor without setter, but it still do a good job', () => {
    const fn = jest.fn();
    const originConsole = console;
    global.console = Object.assign({}, originConsole, {warn: jest.fn()});
    const get = function (value) {
      fn();
      return value;
    };
    const set = get;
    const value = 1;
    class Foo {
      @accessor({get, set})
      get bar () {
        return value;
      }
    };
    const foo = new Foo();
    expect(console.warn).toHaveBeenCalledTimes(1);
    expect(console.warn).lastCalledWith("You are trying to set setter via @accessor on one property without setter. That's not a good idea.");
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(() => {foo.bar = 2;}).not.toThrow();
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(1);
    global.console = originConsole;
  });
  test('@accessor will warn if you pass it a getter function and offer it a accessor without getter, but it still do a good job', () => {
    const fn = jest.fn();
    const originConsole = console;
    global.console = Object.assign({}, originConsole, {warn: jest.fn()});
    const get = function (value) {
      fn();
      return value;
    };
    const set = get;
    let value = 1;
    class Foo {
      @accessor({get, set})
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(console.warn).toHaveBeenCalledTimes(1);
    expect(console.warn).lastCalledWith("You are trying to set getter via @accessor on one property without getter. That's not a good idea.");
    expect(foo.bar).toBe(undefined);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(() => {foo.bar = 2;}).not.toThrow();
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(undefined);
    expect(value).toBe(2);
    global.console = originConsole;
  });
  test('@accessor can just modify descriptor with only getter as you offet get', () => {
    const fn = jest.fn();
    const get = function (value) {
      fn();
      return value;
    };
    const value = 1;
    class Foo {
      @accessor({get})
      get bar () {
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(value);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(() => {foo.bar = 2;}).toThrow('Cannot set property bar of #<Foo> which has only a getter');
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(value);
    expect(foo.bar).toBe(1);
  });
  test('@accessor can just modify descriptor with only setter as you offet set', () => {
    const fn = jest.fn();
    const set = function (value) {
      fn();
      return value;
    };
    let value = 1;
    class Foo {
      @accessor({set})
      set bar (val) {
        value = val;
        return value;
      }
    };
    const foo = new Foo();
    expect(foo.bar).toBe(undefined);
    expect(fn).toHaveBeenCalledTimes(0);
    expect(() => {foo.bar = 2;}).not.toThrow();
    expect(fn).toHaveBeenCalledTimes(1);
    expect(foo.bar).toBe(undefined);
    expect(value).toBe(2);
  });
  test('@accessor alse accept array of funtion as getter/setter', () => {
    const fn = jest.fn();
    const handler = function (value) {
      fn(value);
      expect(this).toBe(foo);
      return value;
    };
    const get = [handler, handler];
    const set = get;
    class Foo {
      @accessor({get, set})
      bar = 1;
    }
    const foo = new Foo();
    expect(foo.bar).toBe(1);
    expect(fn).toHaveBeenCalledTimes(2);
    expect(fn).lastCalledWith(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(4);
    expect(fn).lastCalledWith(2);
    expect(foo.bar).toBe(2);
  });
  test('@accessor will throw error when they accept illgal array', () => {
    expect(() => class {
      @accessor({get: []})
      bar = 1;
    }).toThrow("@accessor need a getter or setter. If you don't need to add setter/getter. You should remove @accessor");
    expect(() => class {
      @accessor({set: []})
      bar = 1;
    }).toThrow("@accessor need a getter or setter. If you don't need to add setter/getter. You should remove @accessor");
    expect(() => class {
      @accessor({get: [1]})
      bar = 1;
    }).toThrow('@accessor only accept function or array of function as getter/setter');
    expect(() => class {
      @accessor({get: [() => {}, 1]})
      bar = 1;
    }).toThrow('@accessor only accept function or array of function as getter/setter');
  });
  test('@accessor accept one function in the array, though we do not courage people to do this', () => {
    const fn = jest.fn();
    const handler = function (value) {
      fn(value);
      expect(this).toBe(foo);
      return value;
    };
    class Foo {
      @accessor({get: [handler], set: [handler]})
      bar = 1;
    }
    const foo = new Foo();
    expect(foo.bar).toBe(1);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(fn).lastCalledWith(1);
    foo.bar = 2;
    expect(fn).toHaveBeenCalledTimes(2);
    expect(foo.bar).toBe(2);
    expect(fn).lastCalledWith(2);
  });
  test('@accessor can accessor undefined', () => {
    let value = 1;
    class Foo {};
    applyDecorators(Foo, {
      a: accessor({
        get () {return value;},
        set (val) {
          value = val;
          return value;
        }
      })
    });
    const foo = new Foo();
    expect(foo.a).toBe(value);
    foo.a = 2;
    expect(foo.a).toBe(value);
    expect(value).toBe(2);
  });
});
<|endoftext|>


<commit_before><commit_msg>Refactor to ES6 imports<commit_after><|endoftext|>


<commit_before><commit_msg>Remove further logs.<commit_after><|endoftext|>


<commit_before>var _ = require("lodash");
var fs = require("fs");
var path = require("path");
var urllib = require("url");
var express = require("express");
var request = require("request");
var bodyParser = require("body-parser");
var compiler = require("krl-compiler");
var version = require("../package.json").version;

var httpGetKRL = function(url, callback){
    request(url, function(err, resp, body){
        if(err)
            return callback(err);
        if(resp.statusCode !== 200)
            return callback(new Error("Got a statusCode=" + resp.statusCode + " for: " + url));

        callback(null, body);
    });
};

var getKRLByURL = function(url, callback){
    var url_parsed = urllib.parse(url);
    if(url_parsed.protocol === "file:"){
        fs.readFile(url_parsed.path, function(err, data){
            if(err) return callback(err);
            callback(null, data.toString());
        });
        return;
    }
    httpGetKRL(url, callback);
};

var mergeGetPost = function(req){
    //give preference to post body params
    return _.assign({}, req.query, req.body);
};

module.exports = function(pe){
    var logs = {};
    var logRID = "io.picolabs.logging";
    var logEntry = function(context,message){
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            episode.logs.push(timestamp+" "+message);
        } else {
            console.log("[ERROR]","no episode found for",eci);
        }
    };
    var logEpisode = function(pico_id,context,callback){
        var eci = context.eci;
        var episode = logs[eci];
        if (!episode) {
            console.log("[ERROR]","no episode found for",eci);
            return;
        }
        pe.db.getEntVar(pico_id,logRID,"status",function(e,status){
            if (status) {
                pe.db.getEntVar(pico_id,logRID,"logs",function(e,data){
                    data[episode.key] = episode.logs;
                    pe.db.putEntVar(pico_id,logRID,"logs",data,function(e){
                        callback(delete logs[eci]);
                    });
                });
            } else {
                callback(delete logs[eci]);
            }
        });
    };
    pe.emitter.on("episode_start", function(context){
        console.log("EPISODE_START",context);
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            console.log("[ERROR]","episode already exists for",eci);
        } else {
            episode = {};
            episode.key = (
                    timestamp + " - " + eci
                    + " - " + ((context.event) ? context.event.eid : "query")
                    ).replace(/[.]/g, "-");
            episode.logs = [];
            logs[eci] = episode;
        }
    });
    pe.emitter.on("klog", function(context, val, message){
        console.log("[KLOG]", message, val);
        logEntry(context,"[KLOG] "+message+" "+JSON.stringify(val));
    });
    pe.emitter.on("log-error", function(context_info, expression){
        console.log("[LOG-ERROR]",context_info,expression);
        logEntry(context_info,"[LOG-ERROR] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-warn", function(context_info, expression){
        console.log("[LOG-WARN]",context_info,expression);
        logEntry(context_info,"[LOG-WARN] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-info", function(context_info, expression){
        console.log("[LOG-INFO]",context_info,expression);
        logEntry(context_info,"[LOG-INFO] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-debug", function(context_info, expression){
        console.log("[LOG-DEBUG]",context_info,expression);
        logEntry(context_info,"[LOG-DEBUG] "+JSON.stringify(expression));
    });
    pe.emitter.on("debug", function(context, message){
        console.log("[DEBUG]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("error", function(context, message){
        console.error("[ERROR]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("episode_stop", function(context){
        console.log("EPISODE_STOP",context);
        var callback = function(outcome){
            console.log("[EPISODE_REMOVED]",outcome);
        };
        logEpisode(context.pico_id,context,callback);
    });

    var app = express();
    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });
    app.use(express.static(path.resolve(__dirname, "..", "public")));
    app.use(bodyParser.json({type: "application/json"}));
    app.use(bodyParser.urlencoded({type: "application/x-www-form-urlencoded", extended: false}));

    var errResp = function(res, err){
        res.status(err.statusCode || 500).json({error: err.message});
    };

    app.all("/sky/event/:eci/:eid/:domain/:type", function(req, res){
        var event = {
            eci: req.params.eci,
            eid: req.params.eid,
            domain: req.params.domain,
            type: req.params.type,
            attrs: mergeGetPost(req)
        };
        pe.signalEvent(event, function(err, response){
            if(err) return errResp(res, err);
            res.json(response);
        });
    });

    app.all("/sky/cloud/:eci/:rid/:function", function(req, res){
        var query = {
            eci: req.params.eci,
            rid: req.params.rid,
            name: req.params["function"],
            args: mergeGetPost(req)
        };
        pe.runQuery(query, function(err, data){
            if(err) return errResp(res, err);
            if(_.isFunction(data)){
                data(res);
            }else{
                res.json(data);
            }
        });
    });

    app.all("/api/engine-version", function(req, res){
        res.json({"version": version});
    });

    app.all("/api/db-dump", function(req, res){
        pe.db.toObj(function(err, db_data){
            if(err) return errResp(res, err);
            res.json(db_data);
        });
    });

    app.all("/api/owner-eci", function(req, res){
        pe.db.getOwnerECI(function(err, eci){
            if(err) return errResp(res, err);
            res.json({ok: true, eci: eci});
        });
    });

    app.all("/api/pico/:id/new-channel", function(req, res){
        var args = mergeGetPost(req);

        pe.db.newChannel({
            pico_id: req.params.id,
            name: args.name,
            type: args.type
        }, function(err, new_channel){
            if(err) return errResp(res, err);
            res.json(new_channel);
        });
    });

    app.all("/api/pico/:id/rm-channel/:eci", function(req, res){
        pe.db.removeChannel(req.params.id, req.params.eci, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ruleset/:rid", function(req, res){
        pe.db.removeRuleset(req.params.id, req.params.rid, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ent-var/:rid/:var_name", function(req, res){
        pe.db.removeEntVar(req.params.id, req.params.rid, req.params.var_name, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/ruleset/compile", function(req, res){
        var args = mergeGetPost(req);

        try{
            res.json({ok: true, code: compiler(args.src).code});
        }catch(err){
            res.status(400).json({ error: err.toString() });
        }
    });

    app.all("/api/ruleset/register", function(req, res){
        var args = mergeGetPost(req);

        var register = function(src, meta){
            pe.registerRuleset(src, meta || {}, function(err, data){
                if(err) return errResp(res, err);
                res.json({ok: true, rid: data.rid, hash: data.hash});
            });
        };
        if(_.isString(args.src)){
            register(args.src);
        }else if(_.isString(args.url)){
            getKRLByURL(args.url, function(err, src){
                if(err) return errResp(res, err);
                register(src, {url: args.url});
            });
        }else{
            errResp(res, new Error("expected `src` or `url`"));
        }
    });

    app.all("/api/ruleset/flush/:rid", function(req, res){
        var rid = req.params.rid;
        pe.db.getEnabledRuleset(rid, function(err, rs_data){
            if(err) return errResp(res, err);

            var url = rs_data.url;
            if(!_.isString(url)){
                errResp(res, new Error("cannot flush a locally registered ruleset"));
            }
            getKRLByURL(url, function(err, src){
                if(err) return errResp(res, err);

                pe.registerRuleset(src, {url: url}, function(err, data){
                    if(err) return errResp(res, err);

                    console.log("Ruleset successfully flushed: " + data.rid);

                    res.json({ok: true, rid: data.rid, hash: data.hash});
                });
            });
        });
    });

    app.all("/api/ruleset/unregister/:rid", function(req, res){
        pe.db.unregisterRuleset(req.params.rid, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    return app;
};
<commit_msg>Match new db ruleset deletion function name<commit_after>var _ = require("lodash");
var fs = require("fs");
var path = require("path");
var urllib = require("url");
var express = require("express");
var request = require("request");
var bodyParser = require("body-parser");
var compiler = require("krl-compiler");
var version = require("../package.json").version;

var httpGetKRL = function(url, callback){
    request(url, function(err, resp, body){
        if(err)
            return callback(err);
        if(resp.statusCode !== 200)
            return callback(new Error("Got a statusCode=" + resp.statusCode + " for: " + url));

        callback(null, body);
    });
};

var getKRLByURL = function(url, callback){
    var url_parsed = urllib.parse(url);
    if(url_parsed.protocol === "file:"){
        fs.readFile(url_parsed.path, function(err, data){
            if(err) return callback(err);
            callback(null, data.toString());
        });
        return;
    }
    httpGetKRL(url, callback);
};

var mergeGetPost = function(req){
    //give preference to post body params
    return _.assign({}, req.query, req.body);
};

module.exports = function(pe){
    var logs = {};
    var logRID = "io.picolabs.logging";
    var logEntry = function(context,message){
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            episode.logs.push(timestamp+" "+message);
        } else {
            console.log("[ERROR]","no episode found for",eci);
        }
    };
    var logEpisode = function(pico_id,context,callback){
        var eci = context.eci;
        var episode = logs[eci];
        if (!episode) {
            console.log("[ERROR]","no episode found for",eci);
            return;
        }
        pe.db.getEntVar(pico_id,logRID,"status",function(e,status){
            if (status) {
                pe.db.getEntVar(pico_id,logRID,"logs",function(e,data){
                    data[episode.key] = episode.logs;
                    pe.db.putEntVar(pico_id,logRID,"logs",data,function(e){
                        callback(delete logs[eci]);
                    });
                });
            } else {
                callback(delete logs[eci]);
            }
        });
    };
    pe.emitter.on("episode_start", function(context){
        console.log("EPISODE_START",context);
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            console.log("[ERROR]","episode already exists for",eci);
        } else {
            episode = {};
            episode.key = (
                    timestamp + " - " + eci
                    + " - " + ((context.event) ? context.event.eid : "query")
                    ).replace(/[.]/g, "-");
            episode.logs = [];
            logs[eci] = episode;
        }
    });
    pe.emitter.on("klog", function(context, val, message){
        console.log("[KLOG]", message, val);
        logEntry(context,"[KLOG] "+message+" "+JSON.stringify(val));
    });
    pe.emitter.on("log-error", function(context_info, expression){
        console.log("[LOG-ERROR]",context_info,expression);
        logEntry(context_info,"[LOG-ERROR] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-warn", function(context_info, expression){
        console.log("[LOG-WARN]",context_info,expression);
        logEntry(context_info,"[LOG-WARN] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-info", function(context_info, expression){
        console.log("[LOG-INFO]",context_info,expression);
        logEntry(context_info,"[LOG-INFO] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-debug", function(context_info, expression){
        console.log("[LOG-DEBUG]",context_info,expression);
        logEntry(context_info,"[LOG-DEBUG] "+JSON.stringify(expression));
    });
    pe.emitter.on("debug", function(context, message){
        console.log("[DEBUG]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("error", function(context, message){
        console.error("[ERROR]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("episode_stop", function(context){
        console.log("EPISODE_STOP",context);
        var callback = function(outcome){
            console.log("[EPISODE_REMOVED]",outcome);
        };
        logEpisode(context.pico_id,context,callback);
    });

    var app = express();
    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });
    app.use(express.static(path.resolve(__dirname, "..", "public")));
    app.use(bodyParser.json({type: "application/json"}));
    app.use(bodyParser.urlencoded({type: "application/x-www-form-urlencoded", extended: false}));

    var errResp = function(res, err){
        res.status(err.statusCode || 500).json({error: err.message});
    };

    app.all("/sky/event/:eci/:eid/:domain/:type", function(req, res){
        var event = {
            eci: req.params.eci,
            eid: req.params.eid,
            domain: req.params.domain,
            type: req.params.type,
            attrs: mergeGetPost(req)
        };
        pe.signalEvent(event, function(err, response){
            if(err) return errResp(res, err);
            res.json(response);
        });
    });

    app.all("/sky/cloud/:eci/:rid/:function", function(req, res){
        var query = {
            eci: req.params.eci,
            rid: req.params.rid,
            name: req.params["function"],
            args: mergeGetPost(req)
        };
        pe.runQuery(query, function(err, data){
            if(err) return errResp(res, err);
            if(_.isFunction(data)){
                data(res);
            }else{
                res.json(data);
            }
        });
    });

    app.all("/api/engine-version", function(req, res){
        res.json({"version": version});
    });

    app.all("/api/db-dump", function(req, res){
        pe.db.toObj(function(err, db_data){
            if(err) return errResp(res, err);
            res.json(db_data);
        });
    });

    app.all("/api/owner-eci", function(req, res){
        pe.db.getOwnerECI(function(err, eci){
            if(err) return errResp(res, err);
            res.json({ok: true, eci: eci});
        });
    });

    app.all("/api/pico/:id/new-channel", function(req, res){
        var args = mergeGetPost(req);

        pe.db.newChannel({
            pico_id: req.params.id,
            name: args.name,
            type: args.type
        }, function(err, new_channel){
            if(err) return errResp(res, err);
            res.json(new_channel);
        });
    });

    app.all("/api/pico/:id/rm-channel/:eci", function(req, res){
        pe.db.removeChannel(req.params.id, req.params.eci, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ruleset/:rid", function(req, res){
        pe.db.removeRuleset(req.params.id, req.params.rid, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ent-var/:rid/:var_name", function(req, res){
        pe.db.removeEntVar(req.params.id, req.params.rid, req.params.var_name, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/ruleset/compile", function(req, res){
        var args = mergeGetPost(req);

        try{
            res.json({ok: true, code: compiler(args.src).code});
        }catch(err){
            res.status(400).json({ error: err.toString() });
        }
    });

    app.all("/api/ruleset/register", function(req, res){
        var args = mergeGetPost(req);

        var register = function(src, meta){
            pe.registerRuleset(src, meta || {}, function(err, data){
                if(err) return errResp(res, err);
                res.json({ok: true, rid: data.rid, hash: data.hash});
            });
        };
        if(_.isString(args.src)){
            register(args.src);
        }else if(_.isString(args.url)){
            getKRLByURL(args.url, function(err, src){
                if(err) return errResp(res, err);
                register(src, {url: args.url});
            });
        }else{
            errResp(res, new Error("expected `src` or `url`"));
        }
    });

    app.all("/api/ruleset/flush/:rid", function(req, res){
        var rid = req.params.rid;
        pe.db.getEnabledRuleset(rid, function(err, rs_data){
            if(err) return errResp(res, err);

            var url = rs_data.url;
            if(!_.isString(url)){
                errResp(res, new Error("cannot flush a locally registered ruleset"));
            }
            getKRLByURL(url, function(err, src){
                if(err) return errResp(res, err);

                pe.registerRuleset(src, {url: url}, function(err, data){
                    if(err) return errResp(res, err);

                    console.log("Ruleset successfully flushed: " + data.rid);

                    res.json({ok: true, rid: data.rid, hash: data.hash});
                });
            });
        });
    });

    app.all("/api/ruleset/unregister/:rid", function(req, res){
        pe.db.deleteRuleset(req.params.rid, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    return app;
};
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import Status from '../components/status';
import { makeGetStatus } from '../selectors';
import {
  replyCompose,
  mentionCompose,
  directCompose,
} from '../actions/compose';
import {
  reblog,
  favourite,
  unreblog,
  unfavourite,
  pin,
  unpin,
} from '../actions/interactions';
import { blockAccount } from '../actions/accounts';
import {
  muteStatus,
  unmuteStatus,
  deleteStatus,
  hideStatus,
  revealStatus,
} from '../actions/statuses';
import { initMuteModal } from '../actions/mutes';
import { initReport } from '../actions/reports';
import { openModal } from '../actions/modal';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { boostModal, deleteModal } from '../initial_state';
import { showAlertForError } from '../actions/alerts';

const messages = defineMessages({
  deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
  deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
  redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
  blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
  replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
  replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
});

const makeMapStateToProps = () => {
  const getStatus = makeGetStatus();

  const mapStateToProps = (state, props) => ({
    status: getStatus(state, props),
  });

  return mapStateToProps;
};

const mapDispatchToProps = (dispatch, { intl }) => ({

  onReply (status, router) {
    dispatch((_, getState) => {
      let state = getState();
      if (state.getIn(['compose', 'text']).trim().length !== 0) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onConfirm: () => dispatch(replyCompose(status, router)),
        }));
      } else {
        dispatch(replyCompose(status, router));
      }
    });
  },

  onModalReblog (status) {
    if (status.get('reblogged')) {
      dispatch(unreblog(status));
    } else {
      dispatch(reblog(status));
    }
  },

  onReblog (status, e) {
    if (e.shiftKey || !boostModal) {
      this.onModalReblog(status);
    } else {
      dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
    }
  },

  onFavourite (status) {
    if (status.get('favourited')) {
      dispatch(unfavourite(status));
    } else {
      dispatch(favourite(status));
    }
  },

  onPin (status) {
    if (status.get('pinned')) {
      dispatch(unpin(status));
    } else {
      dispatch(pin(status));
    }
  },

  onEmbed (status) {
    dispatch(openModal('EMBED', {
      url: status.get('url'),
      onError: error => dispatch(showAlertForError(error)),
    }));
  },

  onDelete (status, history, withRedraft = false) {
    if (!deleteModal) {
      dispatch(deleteStatus(status.get('id'), history, withRedraft));
    } else {
      dispatch(openModal('CONFIRM', {
        message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
        confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
        onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
      }));
    }
  },

  onDirect (account, router) {
    dispatch(directCompose(account, router));
  },

  onMention (account, router) {
    dispatch(mentionCompose(account, router));
  },

  onOpenMedia (media, index) {
    dispatch(openModal('MEDIA', { media, index }));
  },

  onOpenVideo (media, time) {
    dispatch(openModal('VIDEO', { media, time }));
  },

  onBlock (status) {
    const account = status.get('account');
    dispatch(openModal('CONFIRM', {
      message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: @{account.get('acct')} }} />,
      confirm: intl.formatMessage(messages.blockConfirm),
      onConfirm: () => dispatch(blockAccount(account.get('id'))),
      secondary: intl.formatMessage(messages.blockAndReport),
      onSecondary: () => {
        dispatch(blockAccount(account.get('id')));
        dispatch(initReport(account, status));
      },
    }));
  },

  onReport (status) {
    dispatch(initReport(status.get('account'), status));
  },

  onMute (account) {
    dispatch(initMuteModal(account));
  },

  onMuteConversation (status) {
    if (status.get('muted')) {
      dispatch(unmuteStatus(status.get('id')));
    } else {
      dispatch(muteStatus(status.get('id')));
    }
  },

  onToggleHidden (status) {
    if (status.get('hidden')) {
      dispatch(revealStatus(status.get('id')));
    } else {
      dispatch(hideStatus(status.get('id')));
    }
  },

});

export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
<commit_msg>Fix boost to original audience not working on mobile (#11371)<commit_after>import React from 'react';
import { connect } from 'react-redux';
import Status from '../components/status';
import { makeGetStatus } from '../selectors';
import {
  replyCompose,
  mentionCompose,
  directCompose,
} from '../actions/compose';
import {
  reblog,
  favourite,
  unreblog,
  unfavourite,
  pin,
  unpin,
} from '../actions/interactions';
import { blockAccount } from '../actions/accounts';
import {
  muteStatus,
  unmuteStatus,
  deleteStatus,
  hideStatus,
  revealStatus,
} from '../actions/statuses';
import { initMuteModal } from '../actions/mutes';
import { initReport } from '../actions/reports';
import { openModal } from '../actions/modal';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { boostModal, deleteModal } from '../initial_state';
import { showAlertForError } from '../actions/alerts';

const messages = defineMessages({
  deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' },
  deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' },
  redraftConfirm: { id: 'confirmations.redraft.confirm', defaultMessage: 'Delete & redraft' },
  redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
  blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' },
  replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
  replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
  blockAndReport: { id: 'confirmations.block.block_and_report', defaultMessage: 'Block & Report' },
});

const makeMapStateToProps = () => {
  const getStatus = makeGetStatus();

  const mapStateToProps = (state, props) => ({
    status: getStatus(state, props),
  });

  return mapStateToProps;
};

const mapDispatchToProps = (dispatch, { intl }) => ({

  onReply (status, router) {
    dispatch((_, getState) => {
      let state = getState();
      if (state.getIn(['compose', 'text']).trim().length !== 0) {
        dispatch(openModal('CONFIRM', {
          message: intl.formatMessage(messages.replyMessage),
          confirm: intl.formatMessage(messages.replyConfirm),
          onConfirm: () => dispatch(replyCompose(status, router)),
        }));
      } else {
        dispatch(replyCompose(status, router));
      }
    });
  },

  onModalReblog (status) {
    if (status.get('reblogged')) {
      dispatch(unreblog(status));
    } else {
      dispatch(reblog(status));
    }
  },

  onReblog (status, e) {
    if ((e && e.shiftKey) || !boostModal) {
      this.onModalReblog(status);
    } else {
      dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
    }
  },

  onFavourite (status) {
    if (status.get('favourited')) {
      dispatch(unfavourite(status));
    } else {
      dispatch(favourite(status));
    }
  },

  onPin (status) {
    if (status.get('pinned')) {
      dispatch(unpin(status));
    } else {
      dispatch(pin(status));
    }
  },

  onEmbed (status) {
    dispatch(openModal('EMBED', {
      url: status.get('url'),
      onError: error => dispatch(showAlertForError(error)),
    }));
  },

  onDelete (status, history, withRedraft = false) {
    if (!deleteModal) {
      dispatch(deleteStatus(status.get('id'), history, withRedraft));
    } else {
      dispatch(openModal('CONFIRM', {
        message: intl.formatMessage(withRedraft ? messages.redraftMessage : messages.deleteMessage),
        confirm: intl.formatMessage(withRedraft ? messages.redraftConfirm : messages.deleteConfirm),
        onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
      }));
    }
  },

  onDirect (account, router) {
    dispatch(directCompose(account, router));
  },

  onMention (account, router) {
    dispatch(mentionCompose(account, router));
  },

  onOpenMedia (media, index) {
    dispatch(openModal('MEDIA', { media, index }));
  },

  onOpenVideo (media, time) {
    dispatch(openModal('VIDEO', { media, time }));
  },

  onBlock (status) {
    const account = status.get('account');
    dispatch(openModal('CONFIRM', {
      message: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: @{account.get('acct')} }} />,
      confirm: intl.formatMessage(messages.blockConfirm),
      onConfirm: () => dispatch(blockAccount(account.get('id'))),
      secondary: intl.formatMessage(messages.blockAndReport),
      onSecondary: () => {
        dispatch(blockAccount(account.get('id')));
        dispatch(initReport(account, status));
      },
    }));
  },

  onReport (status) {
    dispatch(initReport(status.get('account'), status));
  },

  onMute (account) {
    dispatch(initMuteModal(account));
  },

  onMuteConversation (status) {
    if (status.get('muted')) {
      dispatch(unmuteStatus(status.get('id')));
    } else {
      dispatch(muteStatus(status.get('id')));
    }
  },

  onToggleHidden (status) {
    if (status.get('hidden')) {
      dispatch(revealStatus(status.get('id')));
    } else {
      dispatch(hideStatus(status.get('id')));
    }
  },

});

export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
<|endoftext|>