<commit_before>/**
 *  entry point for table
 */

$(function() {

  var Table = cdb.core.View.extend({

    el: document.body,

    events: {
      'keypress': 'keyPress',
      'keyup': 'keyUp'
    },

    initialize: function(options) {
      this.table = null;
      this.selectedMenu = null; // enable this
      this.workViewActive = 'table';
                                      // for oppening a menu in the startup
      // Get user layers as well
      this.options.user_data.get_layers = true;

      this.user = new cdb.admin.User(this.options.user_data);
      cdb.config.set('user', this.user);

      this._initModels();
      this._initViews();

      cdb.admin.hotkeys.enable();
      this.keyBind();
      this._initBinds();
    },

    /**
    * Bind the keystrokes associated with menu actions
    * alt + <- : show right menu
    * alt + -> : hide right menu
    * alt + c : toggle carto
    * alt + s : toggle sql
    * @method keyBind
    */
    keyBind: function() {
      var self = this;
      cdb.god.bind('hotkey:d', function(e) {
        self.menu.isOpen
          ? self.menu.hide()
          : self.menu.show('sql_mod');
      });
      cdb.god.bind('hotkey:s', function(e) {
        self.menu.show('sql_mod');
      })
      cdb.god.bind('hotkey:c', function(e) {
        self.menu.show('style_mod');
      })
    },

    _initModels: function() {
      var self = this;
      this.vis = new cdb.admin.Visualization(this.options.vis_data);
        // when the user changes slides the visualization is changed
        // but we want to keep the master one for tasks that require to use it
      this.master_vis = new cdb.admin.Visualization(_.omit(this.options.vis_data, 'children'));

      this.vis.setMaster(this.master_vis);

      // if slides are available enable first one to fetch the map
      // if not use the master map
      if (this.vis.slides.length === 0) {
        this.vis.map.set(this.vis.map.parse(this.options.map_data));
      } else {
        this.vis.activeSlide(0);
      }

      this.map = this.vis.map;
      this.vis.enableOverlays();

      var layers = this.options.basemaps;

      this.baseLayers = this.user.layers;
      this.baseLayers.each(function(m) {
        m.set('category', 'Yours');
      })

      _(layers).each(function(catLayers, cat) {
        _(catLayers).map(function(m) {
          var baseTypes = {
            'googlemaps': cdb.admin.GMapsBaseLayer
          }
          var BaseType = baseTypes[m.className] || cdb.admin.TileLayer;

          // Default basemaps are defined in app_config.yml and
          // lib/assets/javascripts/cartodb/table/default_layers.js
          self.baseLayers.add(new BaseType({
            name:        m.name,
            className:   m.className,
            base_type:   m.baseType || 'default',
            urlTemplate: m.url,
            minZoom:     m.minZoom,
            maxZoom:     m.maxZoom,
            attribution: m.attribution,
            subdomains:  m.subdomains,
            baseName:    m.baseName,
            style:       m.style ? JSON.parse(m.style): null,
            labels:      m.labels,
            read_only:   true,
            category:    cat
          }));
        });
      });

      // Background polling model
      // - It takes care of the background imports and geocodings
      this.backgroundPollingModel = new cdb.editor.BackgroundPollingModel({
        geocodingsPolling: true,
        importsPolling: false
      }, {
        user: this.user,
        vis: this.vis
      });
    },

    _resetModel: function(_id) {
      this.vis = new cdb.admin.Visualization({ id: _id });
      this.vis.fetch();
    },

    _initViews: function() {
      this.globalError = new cdb.admin.GlobalError({
        el: $('.globalerror')
      });
      this.globalError.listenGlobal();
      this.addView(this.globalError);

      // ***  Locked visualization (table or visualization type)?
      if (this.vis.get('locked')) {
        var viewModel = new cdb.editor.ChangeLockViewModel({
          items: [this.vis],
          contentType: this.vis.isVisualization() ? 'maps' : 'datasets'
        });
        var view = new cdb.editor.ChangeLockView({
          model: viewModel,
          ownerName: this.vis.permission.owner.get('username'),
          isOwner: this.vis.permission.isOwner(this.user),
          template: cdb.templates.getTemplate('common/dialogs/change_lock/templates/unlock_to_editor'),
          clean_on_hide: true,
          enter_to_confirm: true
        });
        var self = this;
        view.cancel = function() {
          window.location = self.user.viewUrl().dashboard()[ self.vis.isVisualization() ? 'maps' : 'datasets' ]().urlToPath('locked');
        };

        view.appendToBody();
      }

      // ***  tabs
      this.tabs = new cdb.admin.Tabs({
        el: this.$('nav'),
        slash: true
      });
      this.addView(this.tabs);

      // *** work pane (table and map)
      this.workView = new cdb.ui.common.TabPane({
        el: this.$('.panes')
      });

      this.addView(this.workView);

      // *** right menu
      // We need to provide the master/parent vis to allow creating a new visualization
      // and the regular vis to work with the layers
      this.menu = new cdb.admin.LayersPanel({
        vis: this.vis,
        master_vis: this.master_vis,
        user: this.user,
        globalError: this.globalError
      });

      this.$el.append(this.menu.render().el);
      this.menu.hide();
      this.addView(this.menu);

      this.menu.bind('switch', function(layerView) {
        this.setTable(layerView.table, layerView.sqlView);
        if(!this.tableTab) {
          this._initTableMap();
          this.table.trigger('change', this.table);
        }
        this.tableTab.setActiveLayer(layerView);
        this.mapTab.setActiveLayer(layerView);
        this.header.setActiveLayer(layerView);
      }, this);

      // Set watching notifier if needed
      if (!this.vis.isVisualization()) {
        this._setWatchingNotifier();
      }

      // global click
      enableClickOut(this.$el);

      // On resize window...
      $(window).bind("resize", this._onResize);

      // If import layer fail, show dialog
      this.backgroundPollingModel.bind('importLayerFail', function(errorMsg) {
        cdb.editor.ViewFactory.createDialogByTemplate('common/templates/fail', { msg: errorMsg })
        .render().appendToBody();
      });

      this.backgroundPollingModel.bind('geocodingCompleted', function(mdl) {
        // Refresh data in order to have cartodb_georef_status column updated
        // but only if current layer data is the geocoded one
        if (this.table && this.table.get('id') === mdl.get('table_name') && this.table.data) {
          this.table.data().refresh()
        }
        // Refresh map
        if (this.mapTab && this.mapTab.updateDataLayerView) {
          this.mapTab.updateDataLayerView()
        }
        // Reload user data in order to have updated info about geocoding quota etc.
        this.user.fetch();
      }, this);

      this.backgroundPollingModel.bind('geocodingFailed', function() {
        // Refresh data in order to have
        // cartodb_georef_status column updated
        if (this.table && this.table.data) {
          this.table.data().refresh()
        }
      }, this);

      this.backgroundPollingModel.bind('importCompleted', function() {
        // Reload user data in order to have updated info about limits etc.
        this.user.fetch();
      }, this);

      // Background polling view!
      var bgPollingView = new cdb.editor.BackgroundPollingView({
        model: this.backgroundPollingModel,
        createVis: false,
        vis: this.vis,
        user: this.user
      });

      this.$el.append(bgPollingView.render().el);
      this.addView(bgPollingView);

    },

    _initTableMap: function() {
      var self = this;

      // Init geocoder
      // TODO: remove when new_modals is enabled for everybody
      this.geocoder = new cdb.admin.Geocoding();

      // New visualization header
      this.header = new cdb.admin.Header({
        el: this.$('header'),
        globalError: this.globalError,
        model: this.master_vis,
        visualization: this.vis,
        user: this.user,
        config: this.options.config,
        geocoder: this.geocoder,
        backgroundPollingModel: this.backgroundPollingModel
      });
      this.addView(this.header);

      // Table tab
      this.tableTab = new cdb.admin.TableTab({
        model: this.table,
        user: this.user,
        vis: this.vis,
        sqlView: this.sqlView,
        geocoder: this.geocoder,
        backgroundPollingModel: this.backgroundPollingModel,
        globalError: this.globalError,
        menu: this.menu
      });

      // Map tab
      this.mapTab = new cdb.admin.MapTab({
        model: this.map,
        baseLayers: this.baseLayers,
        vis: this.vis,
        master_vis: this.master_vis,
        geocoder: this.geocoder,
        backgroundPollingModel: this.backgroundPollingModel,
        table: this.table,
        user: this.user,
        menu: this.menu
      });

      this._addVideoPlayer();

      this.map.bind('notice', this.globalError.showError, this.globalError);

      this.workView.bind('tabEnabled:map', this.mapTab.enableMap, this.mapTab);
      this.workView.bind('tabEnabled', this.tabs.activate);
      this.mapTab.bind('missingClick', self.menu.hide, self.menu);

      this.workView.addTab('table', this.tableTab.render(), { active: false });
      this.workView.addTab('map', this.mapTab.render(), { active: false });
      this.workView.active(this.workViewActive);
    },

    _initBinds: function() {
      cdb.god.bind('geocodingChosen', this._onGeocodingChosen, this);
    },

    _onGeocodingChosen: function(data) {
      this._sendGeocodingMetrics(data.type);
      if (!data.type === 'lonlat') {
        var geocodeModel = new cdb.editor.LonLatGeocodingModel({
          table: this.table,
          longitude_column: data.longitude,
          latitude_column: data.latitude,
          force_all_rows: !!data.force_all_rows
        });
        this.backgroundPollingModel.addGeocodingItem(geocodeModel);
        return false;
      }

      cdb.log.info('No geocoder option for ' + data.kind);
    },

    _sendGeocodingMetrics: function(type) {
      // TODO: remove mixpanel
      cdb.god.trigger('mixpanel', 'Geocoding', {
        type: type,
        table_name: this.table.get('id')
      });

      // Event tracking "Geocoding"
      cdb.god.trigger('metrics', 'geocoding', {
        email: window.user_data.email
      });
    },

    setTable: function(table, sqlView) {
      var self = this;
      if(this.table) {
        this.table.unbind('notice', null, this.globalError);
        this.table.unbind('change:permission', null, this);
      }

      function setPermissions() {
        // check permissions to set read only
        table.setReadOnly(table.permission.getPermission(self.user) !== cdb.admin.Permission.READ_WRITE);
      }
      table.bind('change:permission', setPermissions, this);
      setPermissions();

      this.table = table;
      this.sqlView = sqlView;
      this.table.bind('notice', this.globalError.showError, this.globalError);
      this.table.bind('change:isSync', this._setSyncInfo, this);
      this._setSyncInfo();
    },

    _addVideoPlayer: function() {

      this.player = new cdb.admin.VideoPlayer();

      if (this.player.hasVideoData()) {
        this.$el.append(this.player.render().$el);
      }

    },

    // Set necessary info if the table is synced
    _setSyncInfo: function() {
      if (this.table && this.table.isSync() && !this.vis.isVisualization()) {
        this.workView.$el.addClass('synced');
      } else {
        this.workView.$el.removeClass('synced');
      }
    },

    _setWatchingNotifier: function() {
      // Create model
      var watchvis_notifier = new cdb.admin.WatchingNotifierModel({}, {
        vis:      this.vis,
        interval: cdb.config.get('watcher_ttl')
      });

      // And then the view
      var watchvis_notifier_view = new cdb.admin.WatchingNotifierView({
        model:  watchvis_notifier,
        user:   this.user
      });

      this.$el.append(watchvis_notifier_view.render().el);
      this.addView(watchvis_notifier_view);
    },

    // Close all dialogs in window resize
    _onResize: function(e) {
      cdb.god.trigger("closeDialogs");
    },

    keyUp: function(e) {},

    keyPress: function(e) {},

    // Show big loader when changes to visualization
    // or table
    showLoader: function(type) {
      this.hideLoader();
      this._loader = cdb.editor.ViewFactory.createDialogByTemplate('common/templates/loading', {
        title: 'Setting ' + type,
        quote: cdb.editor.randomQuote()
      });
      this._loader.appendToBody();
    },

    // Hide big loader when visualization
    // or table finishes
    hideLoader: function() {
      if (this._loader) {
        this._loader.close();
        this._loader = null;
      }
    },

    activeView: function(name) {
      this.workView.active(name);
      // table or map is active?
      this.menu.setActiveWorkView(name);
      this.workViewActive = name;
    }
  });

  cdb._test = cdb._test || {};
  cdb._test.Table = Table;

  cdb.init(function() {
    cdb.config.set(config);
    cdb.config.set('api_key', user_data.api_key);
    cdb.templates.namespace = 'cartodb/';
    cdb.config.set('url_prefix', user_data.base_url);

    var currentUser = new cdb.admin.User(window.user_data);

    if (currentUser.featureEnabled('active_record_geocoding_endpoint')) {
      applyPatchNewGeocodingUrls();
    }
    if (currentUser.featureEnabled('active_record_table_vis_endpoint')) {
      applyPatchNewTableUrls();
    }
    if (currentUser.featureEnabled('active_record_asset_endpoint')) {
      applyPatchNewAssetUrl();
    }

    // Store JS errors
    var errors = new cdb.admin.ErrorStats({ user_data: user_data });

    // Main view
    var table = new Table({
      vis_data: vis_data,
      user_data: user_data,
      config: config,
      map_data: map_data,
      basemaps: basemaps || cdb.admin.DEFAULT_BASEMAPS
    });

    // TODO: remove mixpanel
    if (window.mixpanel) {
      new cdb.admin.Mixpanel({
        user: user_data,
        token: mixpanel_token
      });
    }

    var metrics = new cdb.admin.Metrics();

    // expose to debug
    window.table = table;
    window.table_router = new cdb.admin.TableRouter(table);

    Backbone.history.start({
      pushState: true,
      root: cdb.config.prefixUrlPathname() + '/'
    });
  });

});
<commit_msg>Create geocoding model for non-lonlat types<commit_after>/**
 *  entry point for table
 */

$(function() {

  var Table = cdb.core.View.extend({

    el: document.body,

    events: {
      'keypress': 'keyPress',
      'keyup': 'keyUp'
    },

    initialize: function(options) {
      this.table = null;
      this.selectedMenu = null; // enable this
      this.workViewActive = 'table';
                                      // for oppening a menu in the startup
      // Get user layers as well
      this.options.user_data.get_layers = true;

      this.user = new cdb.admin.User(this.options.user_data);
      cdb.config.set('user', this.user);

      this._initModels();
      this._initViews();

      cdb.admin.hotkeys.enable();
      this.keyBind();
      this._initBinds();
    },

    /**
    * Bind the keystrokes associated with menu actions
    * alt + <- : show right menu
    * alt + -> : hide right menu
    * alt + c : toggle carto
    * alt + s : toggle sql
    * @method keyBind
    */
    keyBind: function() {
      var self = this;
      cdb.god.bind('hotkey:d', function(e) {
        self.menu.isOpen
          ? self.menu.hide()
          : self.menu.show('sql_mod');
      });
      cdb.god.bind('hotkey:s', function(e) {
        self.menu.show('sql_mod');
      })
      cdb.god.bind('hotkey:c', function(e) {
        self.menu.show('style_mod');
      })
    },

    _initModels: function() {
      var self = this;
      this.vis = new cdb.admin.Visualization(this.options.vis_data);
        // when the user changes slides the visualization is changed
        // but we want to keep the master one for tasks that require to use it
      this.master_vis = new cdb.admin.Visualization(_.omit(this.options.vis_data, 'children'));

      this.vis.setMaster(this.master_vis);

      // if slides are available enable first one to fetch the map
      // if not use the master map
      if (this.vis.slides.length === 0) {
        this.vis.map.set(this.vis.map.parse(this.options.map_data));
      } else {
        this.vis.activeSlide(0);
      }

      this.map = this.vis.map;
      this.vis.enableOverlays();

      var layers = this.options.basemaps;

      this.baseLayers = this.user.layers;
      this.baseLayers.each(function(m) {
        m.set('category', 'Yours');
      })

      _(layers).each(function(catLayers, cat) {
        _(catLayers).map(function(m) {
          var baseTypes = {
            'googlemaps': cdb.admin.GMapsBaseLayer
          }
          var BaseType = baseTypes[m.className] || cdb.admin.TileLayer;

          // Default basemaps are defined in app_config.yml and
          // lib/assets/javascripts/cartodb/table/default_layers.js
          self.baseLayers.add(new BaseType({
            name:        m.name,
            className:   m.className,
            base_type:   m.baseType || 'default',
            urlTemplate: m.url,
            minZoom:     m.minZoom,
            maxZoom:     m.maxZoom,
            attribution: m.attribution,
            subdomains:  m.subdomains,
            baseName:    m.baseName,
            style:       m.style ? JSON.parse(m.style): null,
            labels:      m.labels,
            read_only:   true,
            category:    cat
          }));
        });
      });

      // Background polling model
      // - It takes care of the background imports and geocodings
      this.backgroundPollingModel = new cdb.editor.BackgroundPollingModel({
        geocodingsPolling: true,
        importsPolling: false
      }, {
        user: this.user,
        vis: this.vis
      });
    },

    _resetModel: function(_id) {
      this.vis = new cdb.admin.Visualization({ id: _id });
      this.vis.fetch();
    },

    _initViews: function() {
      this.globalError = new cdb.admin.GlobalError({
        el: $('.globalerror')
      });
      this.globalError.listenGlobal();
      this.addView(this.globalError);

      // ***  Locked visualization (table or visualization type)?
      if (this.vis.get('locked')) {
        var viewModel = new cdb.editor.ChangeLockViewModel({
          items: [this.vis],
          contentType: this.vis.isVisualization() ? 'maps' : 'datasets'
        });
        var view = new cdb.editor.ChangeLockView({
          model: viewModel,
          ownerName: this.vis.permission.owner.get('username'),
          isOwner: this.vis.permission.isOwner(this.user),
          template: cdb.templates.getTemplate('common/dialogs/change_lock/templates/unlock_to_editor'),
          clean_on_hide: true,
          enter_to_confirm: true
        });
        var self = this;
        view.cancel = function() {
          window.location = self.user.viewUrl().dashboard()[ self.vis.isVisualization() ? 'maps' : 'datasets' ]().urlToPath('locked');
        };

        view.appendToBody();
      }

      // ***  tabs
      this.tabs = new cdb.admin.Tabs({
        el: this.$('nav'),
        slash: true
      });
      this.addView(this.tabs);

      // *** work pane (table and map)
      this.workView = new cdb.ui.common.TabPane({
        el: this.$('.panes')
      });

      this.addView(this.workView);

      // *** right menu
      // We need to provide the master/parent vis to allow creating a new visualization
      // and the regular vis to work with the layers
      this.menu = new cdb.admin.LayersPanel({
        vis: this.vis,
        master_vis: this.master_vis,
        user: this.user,
        globalError: this.globalError
      });

      this.$el.append(this.menu.render().el);
      this.menu.hide();
      this.addView(this.menu);

      this.menu.bind('switch', function(layerView) {
        this.setTable(layerView.table, layerView.sqlView);
        if(!this.tableTab) {
          this._initTableMap();
          this.table.trigger('change', this.table);
        }
        this.tableTab.setActiveLayer(layerView);
        this.mapTab.setActiveLayer(layerView);
        this.header.setActiveLayer(layerView);
      }, this);

      // Set watching notifier if needed
      if (!this.vis.isVisualization()) {
        this._setWatchingNotifier();
      }

      // global click
      enableClickOut(this.$el);

      // On resize window...
      $(window).bind("resize", this._onResize);

      // If import layer fail, show dialog
      this.backgroundPollingModel.bind('importLayerFail', function(errorMsg) {
        cdb.editor.ViewFactory.createDialogByTemplate('common/templates/fail', { msg: errorMsg })
        .render().appendToBody();
      });

      this.backgroundPollingModel.bind('geocodingCompleted', function(mdl) {
        // Refresh data in order to have cartodb_georef_status column updated
        // but only if current layer data is the geocoded one
        if (this.table && this.table.get('id') === mdl.get('table_name') && this.table.data) {
          this.table.data().refresh()
        }
        // Refresh map
        if (this.mapTab && this.mapTab.updateDataLayerView) {
          this.mapTab.updateDataLayerView()
        }
        // Reload user data in order to have updated info about geocoding quota etc.
        this.user.fetch();
      }, this);

      this.backgroundPollingModel.bind('geocodingFailed', function() {
        // Refresh data in order to have
        // cartodb_georef_status column updated
        if (this.table && this.table.data) {
          this.table.data().refresh()
        }
      }, this);

      this.backgroundPollingModel.bind('importCompleted', function() {
        // Reload user data in order to have updated info about limits etc.
        this.user.fetch();
      }, this);

      // Background polling view!
      var bgPollingView = new cdb.editor.BackgroundPollingView({
        model: this.backgroundPollingModel,
        createVis: false,
        vis: this.vis,
        user: this.user
      });

      this.$el.append(bgPollingView.render().el);
      this.addView(bgPollingView);

    },

    _initTableMap: function() {
      var self = this;

      // Init geocoder
      // TODO: remove when new_modals is enabled for everybody
      this.geocoder = new cdb.admin.Geocoding();

      // New visualization header
      this.header = new cdb.admin.Header({
        el: this.$('header'),
        globalError: this.globalError,
        model: this.master_vis,
        visualization: this.vis,
        user: this.user,
        config: this.options.config,
        geocoder: this.geocoder,
        backgroundPollingModel: this.backgroundPollingModel
      });
      this.addView(this.header);

      // Table tab
      this.tableTab = new cdb.admin.TableTab({
        model: this.table,
        user: this.user,
        vis: this.vis,
        sqlView: this.sqlView,
        geocoder: this.geocoder,
        backgroundPollingModel: this.backgroundPollingModel,
        globalError: this.globalError,
        menu: this.menu
      });

      // Map tab
      this.mapTab = new cdb.admin.MapTab({
        model: this.map,
        baseLayers: this.baseLayers,
        vis: this.vis,
        master_vis: this.master_vis,
        geocoder: this.geocoder,
        backgroundPollingModel: this.backgroundPollingModel,
        table: this.table,
        user: this.user,
        menu: this.menu
      });

      this._addVideoPlayer();

      this.map.bind('notice', this.globalError.showError, this.globalError);

      this.workView.bind('tabEnabled:map', this.mapTab.enableMap, this.mapTab);
      this.workView.bind('tabEnabled', this.tabs.activate);
      this.mapTab.bind('missingClick', self.menu.hide, self.menu);

      this.workView.addTab('table', this.tableTab.render(), { active: false });
      this.workView.addTab('map', this.mapTab.render(), { active: false });
      this.workView.active(this.workViewActive);
    },

    _initBinds: function() {
      cdb.god.bind('geocodingChosen', this._onGeocodingChosen, this);
    },

    _onGeocodingChosen: function(data) {
      this._sendGeocodingMetrics(data.type);

      var geocodeModel;
      if (data.type === 'lonlat') {
        geocodeModel = new cdb.editor.LonLatGeocodingModel({
          table: this.table,
          longitude_column: data.longitude,
          latitude_column: data.latitude,
          force_all_rows: !!data.force_all_rows
        });
      } else {
        geocodeModel = new cdb.editor.GeocodingModel(_.omit(data, 'type'))
      }

      this.backgroundPollingModel.addGeocodingItem(geocodeModel);
    },

    _sendGeocodingMetrics: function(type) {
      // TODO: remove mixpanel
      cdb.god.trigger('mixpanel', 'Geocoding', {
        type: type,
        table_name: this.table.get('id')
      });

      // Event tracking "Geocoding"
      cdb.god.trigger('metrics', 'geocoding', {
        email: window.user_data.email
      });
    },

    setTable: function(table, sqlView) {
      var self = this;
      if(this.table) {
        this.table.unbind('notice', null, this.globalError);
        this.table.unbind('change:permission', null, this);
      }

      function setPermissions() {
        // check permissions to set read only
        table.setReadOnly(table.permission.getPermission(self.user) !== cdb.admin.Permission.READ_WRITE);
      }
      table.bind('change:permission', setPermissions, this);
      setPermissions();

      this.table = table;
      this.sqlView = sqlView;
      this.table.bind('notice', this.globalError.showError, this.globalError);
      this.table.bind('change:isSync', this._setSyncInfo, this);
      this._setSyncInfo();
    },

    _addVideoPlayer: function() {

      this.player = new cdb.admin.VideoPlayer();

      if (this.player.hasVideoData()) {
        this.$el.append(this.player.render().$el);
      }

    },

    // Set necessary info if the table is synced
    _setSyncInfo: function() {
      if (this.table && this.table.isSync() && !this.vis.isVisualization()) {
        this.workView.$el.addClass('synced');
      } else {
        this.workView.$el.removeClass('synced');
      }
    },

    _setWatchingNotifier: function() {
      // Create model
      var watchvis_notifier = new cdb.admin.WatchingNotifierModel({}, {
        vis:      this.vis,
        interval: cdb.config.get('watcher_ttl')
      });

      // And then the view
      var watchvis_notifier_view = new cdb.admin.WatchingNotifierView({
        model:  watchvis_notifier,
        user:   this.user
      });

      this.$el.append(watchvis_notifier_view.render().el);
      this.addView(watchvis_notifier_view);
    },

    // Close all dialogs in window resize
    _onResize: function(e) {
      cdb.god.trigger("closeDialogs");
    },

    keyUp: function(e) {},

    keyPress: function(e) {},

    // Show big loader when changes to visualization
    // or table
    showLoader: function(type) {
      this.hideLoader();
      this._loader = cdb.editor.ViewFactory.createDialogByTemplate('common/templates/loading', {
        title: 'Setting ' + type,
        quote: cdb.editor.randomQuote()
      });
      this._loader.appendToBody();
    },

    // Hide big loader when visualization
    // or table finishes
    hideLoader: function() {
      if (this._loader) {
        this._loader.close();
        this._loader = null;
      }
    },

    activeView: function(name) {
      this.workView.active(name);
      // table or map is active?
      this.menu.setActiveWorkView(name);
      this.workViewActive = name;
    }
  });

  cdb._test = cdb._test || {};
  cdb._test.Table = Table;

  cdb.init(function() {
    cdb.config.set(config);
    cdb.config.set('api_key', user_data.api_key);
    cdb.templates.namespace = 'cartodb/';
    cdb.config.set('url_prefix', user_data.base_url);

    var currentUser = new cdb.admin.User(window.user_data);

    if (currentUser.featureEnabled('active_record_geocoding_endpoint')) {
      applyPatchNewGeocodingUrls();
    }
    if (currentUser.featureEnabled('active_record_table_vis_endpoint')) {
      applyPatchNewTableUrls();
    }
    if (currentUser.featureEnabled('active_record_asset_endpoint')) {
      applyPatchNewAssetUrl();
    }

    // Store JS errors
    var errors = new cdb.admin.ErrorStats({ user_data: user_data });

    // Main view
    var table = new Table({
      vis_data: vis_data,
      user_data: user_data,
      config: config,
      map_data: map_data,
      basemaps: basemaps || cdb.admin.DEFAULT_BASEMAPS
    });

    // TODO: remove mixpanel
    if (window.mixpanel) {
      new cdb.admin.Mixpanel({
        user: user_data,
        token: mixpanel_token
      });
    }

    var metrics = new cdb.admin.Metrics();

    // expose to debug
    window.table = table;
    window.table_router = new cdb.admin.TableRouter(table);

    Backbone.history.start({
      pushState: true,
      root: cdb.config.prefixUrlPathname() + '/'
    });
  });

});
<|endoftext|>


<commit_before>/* File contains code for procedural quest generation. */

const prettybnf = require('prettybnf');
const debug = require('debug')('bitn:quest-gen');
// const fs = require('fs');
const RG = require('../src/rg');
RG.Random = require('../src/random');

const questGrammar = require('./quest-grammar');

const RNG = RG.Random.getRNG();

RNG.setSeed(Date.now());

/* A task represents a part of a quest. */
const Task = function(name) {
    this.type = 'Task';
    this.name = name;
    this.isQuest = () => false;
};

/* A quest object which can be used to model quests. */
const Quest = function() {
    this.steps = []; // Atomics/sub-quests
    this.type = 'Quest';

    this.isQuest = () => true;

    this.add = function(step) {
        if (Array.isArray(step)) {
            this.steps = this.steps.concat(step);
        }
        else {
            this.steps.push(step);
        }
    };

    this.numQuests = function() {
        let sum = 1;
        this.steps.forEach(step => {
            if (step.isQuest && step.isQuest()) {
                sum += 1;
            }
        });
        return sum;
    };

    /* Returns the number of immediate tasks. */
    this.numTasks = function() {
        const numSubquests = this.numQuests() - 1;
        return this.steps.length - numSubquests;
    };
};

/* Code adapted from erratic by Daniel Connelly. */
function extract(prop, o) {
    return o[prop];
}

/* Chooses a random rule from arrOfRules. */
function chooseRandomRule(arrOfRules) {
    if (Array.isArray(arrOfRules)) {
        const result = RNG.arrayGetRand(arrOfRules);
        debug('choose RANDOM', result);
        return result;
    }
    else {
        debug(`chooseRandomRule() not an ARRAY: |${arrOfRules}`);
    }
    return null;
}

/* QuestGen object. */
const QuestGen = function() {
    this._init();
};

/* Can be used for creating quest grammar/rules from a string in BNF format. */
QuestGen.parse = function(grammar) {
    const ast = prettybnf.parse(grammar);
    const rules = {};
    ast.productions.forEach(prod => {
        rules[prod.lhs.text] = prod.rhs.map(extract.bind(null, 'terms'));
    });
    return rules;
};

QuestGen.prototype._init = function() {
    this.stack = []; // Stack for quests
    this.currQuest = null;
    this.ruleHist = {};
    this.startRule = 'QUEST';
};

QuestGen.rules = QuestGen.parse(questGrammar);

/* Default config for quest generation. */
QuestGen.defaultConfig = {
    rules: QuestGen.rules,
    startRule: 'QUEST',
    maxTries: 20,
    debug: false,
    minQuests: 1,
    maxQuests: -1,
    minLength: 1,
    maxLength: -1
};

QuestGen.prototype.genQuestWithConf = function(conf = {}) {
    if (conf.debug) {debug.enabled = true;}
    const questRules = conf.rules || QuestGen.rules;
    const startRule = conf.startRule || 'QUEST';
    this.startRule = startRule;
    let ok = false;
    let watchdog = conf.maxTries || 20;
    let quest = [];
    while (!ok) {
        this._init();

        debug('=== Generating new quest now ===');
        quest = this.generateQuest(questRules, startRule).split('|');
        ok = this._questMeetsReqs(quest, conf);
        if (!ok) {
            debug('QUEST DISCARDED');
        }

        if (--watchdog === 0) {
            console.warn('Could not find a matching quest.');
            break;
        }
    }

    // return quest;
    return this.currQuest;
};

QuestGen.prototype._questMeetsReqs = function(quest, conf) {
    let ok = true;

    // Check if quest min/max length is met
    const minLength = conf.minLength || 1;
    const maxLength = conf.maxLength || -1;
    if (quest.length <= maxLength || maxLength === -1) {
        if (quest.length >= minLength) {
            ok = true;
            debug('MATCHING QUEST FOUND');
        }
        else {ok = false;}
    }
    else {ok = false;}

    if (conf.minQuests) {
        ok = ok && this.currQuest.numQuests() >= conf.minQuests;
    }
    if (conf.maxQuests) {
        ok = ok && this.currQuest.numQuests() <= conf.maxQuests;
    }
    return ok;
};

QuestGen.prototype.generateQuest = function(rules, rule) {
    if (!this.ruleHist[rule]) {this.ruleHist[rule] = 1;}
    else {this.ruleHist[rule] += 1;}

    if (rule === this.startRule) {
        debug('New (sub)-quest will be generated');
        this.currQuest = new Quest();
        this.stack.push(this.currQuest);
    }
    const randRule = chooseRandomRule(rules[rule]);
    if (Array.isArray(randRule)) {
        const steps = randRule.map(this.generateTerm.bind(this, rules));
        // const tasks = steps.map(step => new Task(step));
        // this.currQuest.add(tasks);
        this._checkIfQuestOver(rule);
        return steps.join('|');
    }
    debug(`generateQuest end reached, return |${randRule}|`);
    this._checkIfQuestOver(rule);
    return randRule;
};

QuestGen.prototype.generateTerm = function(rules, term) {
    if (!term.text) {
        const json = JSON.stringify(rules);
        throw new Error('Null/undef term.text with rules|', json, '|');
    }
    if (term.type === 'terminal') {
        this.currQuest.add(new Task(term.text));
        return term.text;
    }
    debug(`calling generate() with term.text |${term.text}|`);
    return this.generateQuest(rules, term.text);
};

QuestGen.prototype._checkIfQuestOver = function(rule) {
    if (rule === this.startRule) {
        debug('Finishing current quest');
        const qLen = this.stack.length;
        if (qLen > 1) {
            const subQuest = this.stack.pop();
            this.currQuest = this.stack[this.stack.length - 1];
            this.currQuest.add(subQuest);
        }
    }
};

const runningAsScript = !module.parent;
if (runningAsScript) {
    // The grammar is stored in the string g
    // console.log(JSON.stringify(rules));
    // console.log(generateQuest(rules, 'QUEST'));
    const questGen = new QuestGen();
    console.log(questGen.genQuestWithConf({maxLength: 10, minLength: 3}));
    console.log(JSON.stringify(questGen.stack));
    console.log(JSON.stringify(questGen.ruleHist));
}

module.exports = {
    Quest, Task, QuestGen
};
<commit_msg>Added some getters/setters to QuestData. Added QuestPopulate.<commit_after>/* File contains code for procedural quest generation. */

const prettybnf = require('prettybnf');
const debug = require('debug')('bitn:quest-gen');
// const fs = require('fs');
const RG = require('../src/rg');
RG.Random = require('../src/random');

const questGrammar = require('./quest-grammar');

const RNG = RG.Random.getRNG();

RNG.setSeed(Date.now());

/* A task represents a part of a quest. */
const Task = function(taskType) {
    this.stepType = 'Task';
    this.name = '';
    this.isQuest = () => false;
    this.taskType = taskType;
};

Task.prototype.getName = function() {
    return this.name;
};

Task.prototype.getTaskType = function() {
    return this.taskType;
};

/* A quest object which can be used to model quests. */
const Quest = function(name) {
    this.name = name;
    this.steps = []; // Atomics/sub-quests
    this.testType = 'Quest';

    this.isQuest = () => true;

    this.add = function(step) {
        if (Array.isArray(step)) {
            this.steps = this.steps.concat(step);
        }
        else {
            this.steps.push(step);
        }
    };

    this.numQuests = function() {
        let sum = 1;
        this.steps.forEach(step => {
            if (step.isQuest && step.isQuest()) {
                sum += 1;
            }
        });
        return sum;
    };

    /* Returns the number of immediate tasks. */
    this.numTasks = function() {
        const numSubquests = this.numQuests() - 1;
        return this.steps.length - numSubquests;
    };

    this.getSteps = function() {
        return this.steps.slice();
    };
};

/* Code adapted from erratic by Daniel Connelly. */
function extract(prop, o) {
    return o[prop];
}

/* Chooses a random rule from arrOfRules. */
function chooseRandomRule(arrOfRules) {
    if (Array.isArray(arrOfRules)) {
        const result = RNG.arrayGetRand(arrOfRules);
        debug('choose RANDOM', result);
        return result;
    }
    else {
        debug(`chooseRandomRule() not an ARRAY: |${arrOfRules}`);
    }
    return null;
}

/* QuestGen object. */
const QuestGen = function() {
    this._init();
};

/* Can be used for creating quest grammar/rules from a string in BNF format. */
QuestGen.parse = function(grammar) {
    const ast = prettybnf.parse(grammar);
    const rules = {};
    ast.productions.forEach(prod => {
        rules[prod.lhs.text] = prod.rhs.map(extract.bind(null, 'terms'));
    });
    return rules;
};

QuestGen.prototype._init = function() {
    this.stack = []; // Stack for quests
    this.currQuest = null;
    this.ruleHist = {};
    this.startRule = 'QUEST';
};

QuestGen.rules = QuestGen.parse(questGrammar);

/* Default config for quest generation. */
QuestGen.defaultConfig = {
    rules: QuestGen.rules,
    startRule: 'QUEST',
    maxTries: 20,
    debug: false,
    minQuests: 1,
    maxQuests: -1,
    minLength: 1,
    maxLength: -1
};

QuestGen.prototype.genQuestWithConf = function(conf = {}) {
    if (conf.debug) {debug.enabled = true;}
    const questRules = conf.rules || QuestGen.rules;
    const startRule = conf.startRule || 'QUEST';
    this.startRule = startRule;
    let ok = false;
    let watchdog = conf.maxTries || 20;
    let quest = [];
    while (!ok) {
        this._init();

        debug('=== Generating new quest now ===');
        quest = this.generateQuest(questRules, startRule).split('|');
        ok = this._questMeetsReqs(quest, conf);
        if (!ok) {
            debug('QUEST DISCARDED');
        }

        if (--watchdog === 0) {
            console.warn('Could not find a matching quest.');
            break;
        }
    }

    // return quest;
    return this.currQuest;
};

QuestGen.prototype.genQuestWithName = function(name) {
    const quest = new Quest(name);
    const taskGoto = new Task('<goto>already_there');
    quest.add(taskGoto);
    const taskKill = new Task('<kill>kill');
    quest.add(taskKill);
    return quest;
};

QuestGen.prototype._questMeetsReqs = function(quest, conf) {
    let ok = true;

    // Check if quest min/max length is met
    const minLength = conf.minLength || 1;
    const maxLength = conf.maxLength || -1;
    if (quest.length <= maxLength || maxLength === -1) {
        if (quest.length >= minLength) {
            ok = true;
            debug('MATCHING QUEST FOUND');
        }
        else {ok = false;}
    }
    else {ok = false;}

    if (conf.minQuests) {
        ok = ok && this.currQuest.numQuests() >= conf.minQuests;
    }
    if (conf.maxQuests) {
        ok = ok && this.currQuest.numQuests() <= conf.maxQuests;
    }
    return ok;
};

QuestGen.prototype.generateQuest = function(rules, rule) {
    if (!this.ruleHist[rule]) {this.ruleHist[rule] = 1;}
    else {this.ruleHist[rule] += 1;}

    if (rule === this.startRule) {
        debug('New (sub)-quest will be generated');
        this.currQuest = new Quest();
        this.stack.push(this.currQuest);
    }
    const randRule = chooseRandomRule(rules[rule]);
    if (Array.isArray(randRule)) {
        const steps = randRule.map(this.generateTerm.bind(this, rules));
        // const tasks = steps.map(step => new Task(step));
        // this.currQuest.add(tasks);
        this._checkIfQuestOver(rule);
        return steps.join('|');
    }
    debug(`generateQuest end reached, return |${randRule}|`);
    this._checkIfQuestOver(rule);
    return randRule;
};

QuestGen.prototype.generateTerm = function(rules, term) {
    if (!term.text) {
        const json = JSON.stringify(rules);
        throw new Error('Null/undef term.text with rules|', json, '|');
    }
    if (term.type === 'terminal') {
        this.currQuest.add(new Task(term.text));
        return term.text;
    }
    debug(`calling generate() with term.text |${term.text}|`);
    return this.generateQuest(rules, term.text);
};

QuestGen.prototype._checkIfQuestOver = function(rule) {
    if (rule === this.startRule) {
        debug('Finishing current quest');
        const qLen = this.stack.length;
        if (qLen > 1) {
            const subQuest = this.stack.pop();
            this.currQuest = this.stack[this.stack.length - 1];
            this.currQuest.add(subQuest);
        }
    }
};

const QuestData = function() {
    this.stacks = {};
    this.path = [];
    this._ptr = {}; // Pointers for iteration
};

QuestData.prototype.pop = function(targetType) {
    if (this.stacks[targetType]) {
        return this.stacks[targetType].pop();
    }
    return null;
};

QuestData.prototype.next = function(targetType) {
    if (this.stacks[targetType]) {
        if (!this._ptr.hasOwnProperty(targetType)) {
            this._ptr[targetType] = 0;
        }
        const ptrVal = this._ptr[targetType];
        if (ptrVal < this.stacks[targetType].length) {
            ++this._ptr[targetType];
            return this.stacks[targetType][ptrVal];
        }
    }
    return null;
};

QuestData.prototype.getCurrent = function(targetType) {
    if (this.stacks[targetType]) {
        const stack = this.stacks[targetType];
        return stack[stack.length - 1];
    }
    return null;
};

QuestData.prototype.add = function(targetType, obj) {
    if (!this.stacks[targetType]) {
        this.stacks[targetType] = obj;
    }
    this.stacks[targetType].push(obj);
    this.path.push(targetType);
};

/* Returns human-readable description of the quest. */
QuestData.prototype.toString = function() {
    let res = '';
    this.path.forEach(step => {
        res += step + ' ' + this.next(step).getName() + '. ';
    });
    return res;
};

//---------------------------------------------------------------------------
// OBJECT QUEST-POPULATE
//---------------------------------------------------------------------------

const QuestPopulate = function() {
    this.questData = {
        quests: [] // Stack of quests, current is the last
    };
};

QuestPopulate.prototype.createQuests = function(world, area, x, y) {
    const areaTile = area.getTileXY(x, y);
    const cities = areaTile.getZones('City');
    cities.forEach(city => {
        this.createQuestsForZone(city, areaTile);
    });
};

QuestPopulate.prototype.createQuestsForZone = function(zone, areaTile) {
    const numQuests = 1;
    for (let i = 0; i < numQuests; i++) {
        const questGen = new QuestGen();
        const quest = questGen.genQuestWithConf({maxLength: 10, minLength: 1});
        this.questData = {quests: []};
        this.mapQuestToResources(quest, zone, areaTile);
    }
};

QuestPopulate.prototype.mapQuestToResources = function(quest, zone, areaTile) {
    this.currQuest = new QuestData();
    this.questData.quests.push(this.currQuest);
    this.currQuest.add('location', zone);
    quest.getSteps().forEach(step => {
        const currLoc = this.currQuest.getCurrent('location');
        if (step.isQuest()) {
            // Recursive call for sub-quests, check which is the current
            // location for the quest
            this.mapQuestToResources(step, currLoc, areaTile);
        }
        else {
            this.mapTask(quest, step, currLoc, areaTile);
        }
    });
    if (this.questData.quests.length > 1) {
        this.currQuest = this.questData.quests.pop();
    }
    // Finally, add a quest to quest giver
    console.log('Created quest: ' + this.currQuest.toString());
};

/* Maps a single task to resources. Prev. or next step may also affect mapping.
 * */
QuestPopulate.prototype.mapTask = function(quest, task, zone, areaTile) {
    switch (task.getTaskType()) {
        case '<kill>kill': {
            const location = this.currQuest.getCurrent('location');
            const level = location.getLevels()[0];
            const actorToKill = RNG.arrayGetRand(level.getActors());
            this.currQuest.add('kill', actorToKill);
            break;
        }
        case '<goto>already_there': {
            this.currQuest.add('location', zone);
            break;
        }
        case '<goto>goto': {
            const newLocation = this.getNewLocation(zone, areaTile);
            this.currQuest.add('location', newLocation);
            break;
        }
        default: {
            console.log(`Task type ${task.taskType} not supported yet`);
        }
    }
};

const runningAsScript = !module.parent;
if (runningAsScript) {
    // The grammar is stored in the string g
    // console.log(JSON.stringify(rules));
    // console.log(generateQuest(rules, 'QUEST'));
    const questGen = new QuestGen();
    console.log(questGen.genQuestWithConf({maxLength: 10, minLength: 3}));
    console.log(JSON.stringify(questGen.stack));
    console.log(JSON.stringify(questGen.ruleHist));
}

module.exports = {
    Quest, Task, QuestGen, QuestPopulate
};
<|endoftext|>


<commit_before>var app = angular.module('vroomApp');

var StationListController = function($scope, $location, $timeout, myGasFeed, testGasFeed) {

    var keywords = {"chevron": true,
        "shell": true,
        "cheap": true,
        "cheapest": true,
        "close": true,
        "closest": true,
        "near": true,
        "nearest": true};

    var chevronKey = {"chevron": true,
        "Chevron": true,
        "cheap": true,
        "cheapest": true};

    var shellKey = {"shell": true,
        "close": true,
        "closest": true,
        "near": true,
        "nearest": true};

    var recognizer = new webkitSpeechRecognition(); // jshint ignore:line
    recognizer.onstart = function() {
        $scope.isListening = true;
        var foot = document.getElementsByClassName('footer')[0];
        var h1 = document.createElement("span");
        h1.className = 'speak-now';
        var text = document.createTextNode("speak now");
        h1.appendChild(text);
        foot.appendChild(h1);

        $scope.shrinkMic();
        console.log('speech started');
    };

    recognizer.onend = function() {
        $scope.isListening = false;
        console.log('speech ended');
        var footer = document.getElementsByClassName('footer')[0];
        var speakLabel = document.getElementsByClassName('speak-now')[0];
        console.log(speakLabel);
        footer.removeChild(speakLabel);

    };
    recognizer.continuous = true;
    recognizer.interimResults = true;
    recognizer.lang = "en";
    recognizer.onresult = function(e) {
        if(e.results.length) {
            var lastResultIndex = e.results.length - 1;
            var words = e.results[lastResultIndex][0].transcript.split(" ");
            $scope.receivedWords = words[words.length - 1];
            console.log('words is: ' + words);
            words.forEach(function(element) {
                var ans = element.toLowerCase();
                console.log(ans + ' in keywords is ' + keywords[element]);
                if(keywords[ans]) {
                    console.log(ans + " was said");

                    var resultImage = new Image();
                    resultImage.src = "images/check-symbol-green.png";
                    resultImage.className = "selection";
                    if(chevronKey[ans]) {
                        var chevron = document.getElementsByClassName("chevron")[0];
                        chevron.appendChild(resultImage);
                    } else {
                        var shell = document.getElementsByClassName("shell")[0];
                        shell.appendChild(resultImage);
                    }
                    //
                    //// route to gas station list
                    //

                    setTimeout(function() {
                        $scope.$apply(function() {
                            recognizer.stop();
                            $location.path("/");
                        });
                    }, 2000);
                }
            });
        }
    };

    var micToggle = true;
    var micIsBig = micToggle ? "mic-icon big" : "mic-icon small";

    $scope.animateMic = function() {
        micToggle = !micToggle;
        micIsBig = micToggle ? "mic-icon big" : "mic-icon small";
        var mic = document.getElementsByClassName('mic-icon')[0];
        mic.className = micIsBig;
    };

    $scope.enlargeMic = function() {
        if($scope.isListening) {
            $scope.animateMic();
            $timeout(function() {
                $scope.shrinkMic();
            }, 600);
        }
    };

    $scope.shrinkMic = function() {
        if($scope.isListening) {
            $scope.animateMic();
            $timeout(function() {
                $scope.enlargeMic();
            }, 600);

        }
    };

    $scope.listen = function() {
        if($scope.isListening) {
            $scope.isListening = false;
            recognizer.stop();
        }
        console.log('listening');
        recognizer.start();
    };

    //
    // My Gas Buddy
    //

    var onGetNearbyGasStationsComplete = function(data) {
        $scope.gasStations = data;
        data.forEach(function(element){
            console.log(element);
        });
    };

    var onError = function(reason) {
        $scope.error = "Could not fetch gas stations";
    };

    //myGasFeed.getNearbyGasStations(latitude, longitude, 3, 'reg', 'price')
    //    .then(onGetNearbyGasStationsComplete, onError);

    var gasStationDataPrice = testGasFeed.getNearbyGasStationsByPrice();
    var gasStationDataDistance = testGasFeed.getNearbyGasStationsByDistance();
    //gasStationData.stations.forEach(function(station) {
    //    console.log(station);
    //});
    //

    $scope.cheapestStation = gasStationDataPrice.stations[0].station;
    $scope.cheapestPrice = gasStationDataPrice.stations[0].reg_price;
    $scope.cheapestDistance = gasStationDataPrice.stations[0].distance;
    $scope.closestStation = gasStationDataDistance.stations[2].station;
    $scope.closestPrice = gasStationDataDistance.stations[2].reg_price;
    $scope.closestDistance = gasStationDataDistance.stations[2].distance;

    console.log(gasStationDataPrice.stations[0].station);
    console.log(gasStationDataPrice.stations[0].reg_price);
    console.log(gasStationDataPrice.stations[0].distance);
    console.log(gasStationDataDistance.stations[2].station);
    console.log(gasStationDataDistance.stations[2].reg_price);
    console.log(gasStationDataDistance.stations[2].distance);

};

app.controller("StationListController", StationListController);
<commit_msg>reroute station list from loading to wayfinding<commit_after>var app = angular.module('vroomApp');

var StationListController = function($scope, $location, $timeout, myGasFeed, testGasFeed) {

    var keywords = {"chevron": true,
        "shell": true,
        "cheap": true,
        "cheapest": true,
        "close": true,
        "closest": true,
        "near": true,
        "nearest": true};

    var chevronKey = {"chevron": true,
        "Chevron": true,
        "cheap": true,
        "cheapest": true};

    var shellKey = {"shell": true,
        "close": true,
        "closest": true,
        "near": true,
        "nearest": true};

    var recognizer = new webkitSpeechRecognition(); // jshint ignore:line
    recognizer.onstart = function() {
        $scope.isListening = true;
        var foot = document.getElementsByClassName('footer')[0];
        var h1 = document.createElement("span");
        h1.className = 'speak-now';
        var text = document.createTextNode("speak now");
        h1.appendChild(text);
        foot.appendChild(h1);

        $scope.shrinkMic();
        console.log('speech started');
    };

    recognizer.onend = function() {
        $scope.isListening = false;
        console.log('speech ended');
        var footer = document.getElementsByClassName('footer')[0];
        var speakLabel = document.getElementsByClassName('speak-now')[0];
        console.log(speakLabel);
        footer.removeChild(speakLabel);

    };
    recognizer.continuous = true;
    recognizer.interimResults = true;
    recognizer.lang = "en";
    recognizer.onresult = function(e) {
        if(e.results.length) {
            var lastResultIndex = e.results.length - 1;
            var words = e.results[lastResultIndex][0].transcript.split(" ");
            $scope.receivedWords = words[words.length - 1];
            console.log('words is: ' + words);
            words.forEach(function(element) {
                var ans = element.toLowerCase();
                console.log(ans + ' in keywords is ' + keywords[element]);
                if(keywords[ans]) {
                    console.log(ans + " was said");

                    var resultImage = new Image();
                    resultImage.src = "images/check-symbol-green.png";
                    resultImage.className = "selection";
                    if(chevronKey[ans]) {
                        var chevron = document.getElementsByClassName("chevron")[0];
                        chevron.appendChild(resultImage);
                    } else {
                        var shell = document.getElementsByClassName("shell")[0];
                        shell.appendChild(resultImage);
                    }
                    //
                    //// route to gas station list
                    //

                    setTimeout(function() {
                        $scope.$apply(function() {
                            recognizer.stop();
                            $location.path("/wayfinding");
                        });
                    }, 2000);
                }
            });
        }
    };

    var micToggle = true;
    var micIsBig = micToggle ? "mic-icon big" : "mic-icon small";

    $scope.animateMic = function() {
        micToggle = !micToggle;
        micIsBig = micToggle ? "mic-icon big" : "mic-icon small";
        var mic = document.getElementsByClassName('mic-icon')[0];
        mic.className = micIsBig;
    };

    $scope.enlargeMic = function() {
        if($scope.isListening) {
            $scope.animateMic();
            $timeout(function() {
                $scope.shrinkMic();
            }, 600);
        }
    };

    $scope.shrinkMic = function() {
        if($scope.isListening) {
            $scope.animateMic();
            $timeout(function() {
                $scope.enlargeMic();
            }, 600);

        }
    };

    $scope.listen = function() {
        if($scope.isListening) {
            $scope.isListening = false;
            recognizer.stop();
        }
        console.log('listening');
        recognizer.start();
    };

    //
    // My Gas Buddy
    //

    var onGetNearbyGasStationsComplete = function(data) {
        $scope.gasStations = data;
        data.forEach(function(element){
            console.log(element);
        });
    };

    var onError = function(reason) {
        $scope.error = "Could not fetch gas stations";
    };

    //myGasFeed.getNearbyGasStations(latitude, longitude, 3, 'reg', 'price')
    //    .then(onGetNearbyGasStationsComplete, onError);

    var gasStationDataPrice = testGasFeed.getNearbyGasStationsByPrice();
    var gasStationDataDistance = testGasFeed.getNearbyGasStationsByDistance();
    //gasStationData.stations.forEach(function(station) {
    //    console.log(station);
    //});
    //

    $scope.cheapestStation = gasStationDataPrice.stations[0].station;
    $scope.cheapestPrice = gasStationDataPrice.stations[0].reg_price;
    $scope.cheapestDistance = gasStationDataPrice.stations[0].distance;
    $scope.closestStation = gasStationDataDistance.stations[2].station;
    $scope.closestPrice = gasStationDataDistance.stations[2].reg_price;
    $scope.closestDistance = gasStationDataDistance.stations[2].distance;

    console.log(gasStationDataPrice.stations[0].station);
    console.log(gasStationDataPrice.stations[0].reg_price);
    console.log(gasStationDataPrice.stations[0].distance);
    console.log(gasStationDataDistance.stations[2].station);
    console.log(gasStationDataDistance.stations[2].reg_price);
    console.log(gasStationDataDistance.stations[2].distance);

};

app.controller("StationListController", StationListController);
<|endoftext|>


<commit_before>"use strict";

var renderLoop;
(function($){
	$(document).ready(function (){
		console.log("main.js");
		setup();
		renderLoop = setInterval(update, 30);
		$(document).keydown(handleInput);
		$(document).keyup(handleInput);
	});
})(jQuery);

window["red"] = {};
window["blue"] = {};
var field = d3.select('#field');
var missileTimeout = 2250;
var fireRateLimit = 100;
var fieldWidth = 800;
var fieldHeight = 600;
Math.radians = function(degrees) {
	return degrees * Math.PI / 180;
};
Math.degrees = function(radians) {
	return radians * 180 / Math.PI;
};

function setup() {
	red.x = 50;
	red.y = Math.floor((fieldHeight-100)*Math.random())+50;
	red.rot = 90;
	red.xv = 0.0;
	red.yv = 0.0;
	red.fireTime = new Date() - 1000;
	red.missileReady = true;
	updateGraphics("red");
	//red.attr("transform","translate("+red.x+","+red.y+"),rotate("+red.rot+")");

	blue.x = fieldWidth-50;
	blue.y = Math.floor((fieldHeight-100)*Math.random())+50;
	blue.rot = -90;
	blue.xv = 0.0;
	blue.yv = 0.0;
	blue.fireTime = new Date() - 1000;
	blue.missileReady = true;
	updateGraphics("blue");
	//blue.attr("transform","translate("+blue.x+","+blue.y+"),rotate("+blue.rot+")");
}

function updatePosition(team){
	var teamObj = window[team];
	if (teamObj.xv*teamObj.xv + teamObj.yv*teamObj.yv > 225){
		teamObj.xv = 15.*teamObj.xv/Math.sqrt(teamObj.xv*teamObj.xv + teamObj.yv*teamObj.yv);
		teamObj.yv = 15.*teamObj.yv/Math.sqrt(teamObj.xv*teamObj.xv + teamObj.yv*teamObj.yv);
	}
	teamObj.x += teamObj.xv;
	teamObj.x = (teamObj.x+fieldWidth)%fieldWidth;
	teamObj.y += teamObj.yv;
	teamObj.y = (teamObj.y+fieldHeight)%fieldHeight;
}

function updateGraphics(team){
	var teamObj = window[team];
	d3.select("#"+team).attr("transform","translate("+teamObj.x+","+teamObj.y+"),rotate("+teamObj.rot+")");
}

function update() {
	checkKeys();

	if (missiles.length){
		var filteredMissiles = [];
		for (var i=0; i<missiles.length; i++) {
			var m = missiles[i];
			if (new Date() - m.time < missileTimeout){
				m.x += m.xv;
				m.x = (m.x+fieldWidth)%fieldWidth;
				m.y += m.yv;
				m.y = (m.y+fieldHeight)%fieldHeight;
				filteredMissiles.push(m);
			}
		}
		missiles = filteredMissiles;

		var dots = d3.select("#field").selectAll('.missile').data(missiles);
		dots.attr("cx", function(d){ return d.x; })
			.attr("cy", function(d){ return d.y; });
		dots.exit().remove();
	}

	updatePosition("red");
	updateGraphics("red");

	updatePosition("blue");
	updateGraphics("blue");
}

function teamMove(team,action) {
	var teamObj = window[team];
	switch (action){
		case "thrust":
			teamObj.xv += 0.5*Math.cos(Math.radians(teamObj.rot-90));
			teamObj.yv += 0.5*Math.sin(Math.radians(teamObj.rot-90));
			break;
		case "fire":
			fireMissile(team);
			break;
		case "turn right":
			teamObj.rot = teamObj.rot + 5;
			break;
		case "turn left":
			teamObj.rot = teamObj.rot - 5;
			break;
		case "hyperspace":
			break;
	}
}

var missiles = [];
/* class Missile {
	constructor(x,y,xv,yv) {
		this.x = x;
		this.y = y;
		this.xv = xv;
		this.yv = yv;
		if (missiles.length > 0){
			this.id = missiles[missiles.length-1].id + 1;
		} else {
			this.id = 1;
		}
		this.name = "missile"+this.id;
	}
} */

function fireMissile(team) {
	var mx,my,mxv,myv;
	var p = window[team];
	mx = p.x;
	my = p.y;
	mxv = p.xv + 10*Math.cos(Math.radians(p.rot-90));
	myv = p.yv + 10*Math.sin(Math.radians(p.rot-90));

	// missiles.push(new Missile(mx,my,mxv,myv));
	missiles.push({'x':mx, 'y':my, 'xv':mxv, 'yv':myv, 'time':new Date()});
	missiles[missiles.length-1]['id'] = missiles.length;

	d3.select("#field").selectAll(".missile")
		.data(missiles)
	  .enter().append("circle")
		.attr("cx", function(d){ return d.x; })
		.attr("cy", function(d){ return d.y; })
		.attr("r", 2)
		.style("fill","white")
		.attr("class", "missile");
}

var keystates = {};
function handleInput(event) {
	if (event.which == 27){
		clearInterval(renderLoop);
		renderLoop = false;
		return;
	} else if (event.which == 13){
		event.preventDefault();
		if (!renderLoop){ renderLoop = setInterval(update, 30); }
		return;
	}

	if (event.which == 191){ event.preventDefault(); };

	if (event.type == 'keydown'){ keystates[event.which] = true;  }
	if (event.type == 'keyup')  {
		keystates[event.which] = false;

		if (event.which == 66) {
			window["red"].missileReady = true;
		} else if (event.which == 191) {
			window["blue"].missileReady = true;
		}
	}
}

var keysOfInterest = [
			90,88,67,86,66,
			78,77,188,190,191
		     ];
function checkKeys() {
	keysOfInterest.forEach(function(k){
		if (keystates[k]){
			switch (k){
				// RED
				case 90:
					teamMove("red","turn left");
					break;
				case 88:
					teamMove("red","turn right");
					break;
				case 67:
					teamMove("red","hyperspace");
					break;
				case 86:
					teamMove("red","thrust");
					break;
				case 66:
					if (new Date() - window["red"].fireTime > fireRateLimit && window["red"].missileReady) {
						teamMove("red","fire");
						window["red"].fireTime = new Date();
						window["red"].missileReady = false;
					}
					break;

				// BLUE
				case 78:
					teamMove("blue","turn left");
					break;
				case 77:
					teamMove("blue","turn right");
					break;
				case 188:
					teamMove("blue","hyperspace");
					break;
				case 190:
					teamMove("blue","thrust");
					break;
				case 191:
					teamMove("blue","fire");
					if (new Date() - window["blue"].fireTime > fireRateLimit && window["blue"].missileReady) {
						teamMove("blue","fire");
						window["blue"].fireTime = new Date();
						window["blue"].missileReady = false;
					}
					break;
			}
		}
	});
}
<commit_msg>fix bug<commit_after>"use strict";

var renderLoop;
(function($){
	$(document).ready(function (){
		console.log("main.js");
		setup();
		renderLoop = setInterval(update, 30);
		$(document).keydown(handleInput);
		$(document).keyup(handleInput);
	});
})(jQuery);

window["red"] = {};
window["blue"] = {};
var field = d3.select('#field');
var missileTimeout = 2250;
var fireRateLimit = 100;
var fieldWidth = 800;
var fieldHeight = 600;
Math.radians = function(degrees) {
	return degrees * Math.PI / 180;
};
Math.degrees = function(radians) {
	return radians * 180 / Math.PI;
};

function setup() {
	red.x = 50;
	red.y = Math.floor((fieldHeight-100)*Math.random())+50;
	red.rot = 90;
	red.xv = 0.0;
	red.yv = 0.0;
	red.fireTime = new Date() - 1000;
	red.missileReady = true;
	updateGraphics("red");
	//red.attr("transform","translate("+red.x+","+red.y+"),rotate("+red.rot+")");

	blue.x = fieldWidth-50;
	blue.y = Math.floor((fieldHeight-100)*Math.random())+50;
	blue.rot = -90;
	blue.xv = 0.0;
	blue.yv = 0.0;
	blue.fireTime = new Date() - 1000;
	blue.missileReady = true;
	updateGraphics("blue");
	//blue.attr("transform","translate("+blue.x+","+blue.y+"),rotate("+blue.rot+")");
}

function updatePosition(team){
	var teamObj = window[team];
	if (teamObj.xv*teamObj.xv + teamObj.yv*teamObj.yv > 225){
		teamObj.xv = 15.*teamObj.xv/Math.sqrt(teamObj.xv*teamObj.xv + teamObj.yv*teamObj.yv);
		teamObj.yv = 15.*teamObj.yv/Math.sqrt(teamObj.xv*teamObj.xv + teamObj.yv*teamObj.yv);
	}
	teamObj.x += teamObj.xv;
	teamObj.x = (teamObj.x+fieldWidth)%fieldWidth;
	teamObj.y += teamObj.yv;
	teamObj.y = (teamObj.y+fieldHeight)%fieldHeight;
}

function updateGraphics(team){
	var teamObj = window[team];
	d3.select("#"+team).attr("transform","translate("+teamObj.x+","+teamObj.y+"),rotate("+teamObj.rot+")");
}

function update() {
	checkKeys();

	if (missiles.length){
		var filteredMissiles = [];
		for (var i=0; i<missiles.length; i++) {
			var m = missiles[i];
			if (new Date() - m.time < missileTimeout){
				m.x += m.xv;
				m.x = (m.x+fieldWidth)%fieldWidth;
				m.y += m.yv;
				m.y = (m.y+fieldHeight)%fieldHeight;
				filteredMissiles.push(m);
			}
		}
		missiles = filteredMissiles;

		var dots = d3.select("#field").selectAll('.missile').data(missiles);
		dots.attr("cx", function(d){ return d.x; })
			.attr("cy", function(d){ return d.y; });
		dots.exit().remove();
	}

	updatePosition("red");
	updateGraphics("red");

	updatePosition("blue");
	updateGraphics("blue");
}

function teamMove(team,action) {
	var teamObj = window[team];
	switch (action){
		case "thrust":
			teamObj.xv += 0.5*Math.cos(Math.radians(teamObj.rot-90));
			teamObj.yv += 0.5*Math.sin(Math.radians(teamObj.rot-90));
			break;
		case "fire":
			fireMissile(team);
			break;
		case "turn right":
			teamObj.rot = teamObj.rot + 5;
			break;
		case "turn left":
			teamObj.rot = teamObj.rot - 5;
			break;
		case "hyperspace":
			break;
	}
}

var missiles = [];
/* class Missile {
	constructor(x,y,xv,yv) {
		this.x = x;
		this.y = y;
		this.xv = xv;
		this.yv = yv;
		if (missiles.length > 0){
			this.id = missiles[missiles.length-1].id + 1;
		} else {
			this.id = 1;
		}
		this.name = "missile"+this.id;
	}
} */

function fireMissile(team) {
	var mx,my,mxv,myv;
	var p = window[team];
	mx = p.x;
	my = p.y;
	mxv = p.xv + 10*Math.cos(Math.radians(p.rot-90));
	myv = p.yv + 10*Math.sin(Math.radians(p.rot-90));

	// missiles.push(new Missile(mx,my,mxv,myv));
	missiles.push({'x':mx, 'y':my, 'xv':mxv, 'yv':myv, 'time':new Date()});
	missiles[missiles.length-1]['id'] = missiles.length;

	d3.select("#field").selectAll(".missile")
		.data(missiles)
	  .enter().append("circle")
		.attr("cx", function(d){ return d.x; })
		.attr("cy", function(d){ return d.y; })
		.attr("r", 2)
		.style("fill","white")
		.attr("class", "missile");
}

var keystates = {};
function handleInput(event) {
	if (event.which == 27){
		clearInterval(renderLoop);
		renderLoop = false;
		return;
	} else if (event.which == 13){
		event.preventDefault();
		if (!renderLoop){ renderLoop = setInterval(update, 30); }
		return;
	}

	if (event.which == 191){ event.preventDefault(); };

	if (event.type == 'keydown'){ keystates[event.which] = true;  }
	if (event.type == 'keyup')  {
		keystates[event.which] = false;

		if (event.which == 66) {
			window["red"].missileReady = true;
		} else if (event.which == 191) {
			window["blue"].missileReady = true;
		}
	}
}

var keysOfInterest = [
			90,88,67,86,66,
			78,77,188,190,191
		     ];
function checkKeys() {
	keysOfInterest.forEach(function(k){
		if (keystates[k]){
			switch (k){
				// RED
				case 90:
					teamMove("red","turn left");
					break;
				case 88:
					teamMove("red","turn right");
					break;
				case 67:
					teamMove("red","hyperspace");
					break;
				case 86:
					teamMove("red","thrust");
					break;
				case 66:
					if (new Date() - window["red"].fireTime > fireRateLimit && window["red"].missileReady) {
						teamMove("red","fire");
						window["red"].fireTime = new Date();
						window["red"].missileReady = false;
					}
					break;

				// BLUE
				case 78:
					teamMove("blue","turn left");
					break;
				case 77:
					teamMove("blue","turn right");
					break;
				case 188:
					teamMove("blue","hyperspace");
					break;
				case 190:
					teamMove("blue","thrust");
					break;
				case 191:
					if (new Date() - window["blue"].fireTime > fireRateLimit && window["blue"].missileReady) {
						teamMove("blue","fire");
						window["blue"].fireTime = new Date();
						window["blue"].missileReady = false;
					}
					break;
			}
		}
	});
}
<|endoftext|>


<commit_before>import React from 'react';
import _ from 'lodash';
import plugins from '../../../utils/plugins';
import async from 'async';
import MetaInspector from 'node-metainspector';
import ls from 'local-storage';
import ModalActions from '../../Modal/actions';

var skipScroll;

var titleCase = str => {
   var splitStr = str.toLowerCase().split(' ');
   for (var i = 0; i < splitStr.length; i++) {
       splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1);
   }
   return splitStr.join(' ');
}

var normalizeUrl = (image, url) => {
    var protocol = url.substr(0, url.indexOf(':'));
    if (image.startsWith('//')) image = protocol + ':' + image;
    if (image.startsWith('/')) image = url + image.substr(1);
    return image;
}

var scanHtml = (html, url, target, attr, searcher) => {
    var image = false;
    var imgs = html(target);
    if (imgs && imgs.length) {
        _.some( imgs, el => {
            var imageUrl = html(el).attr(attr);
            if (imageUrl && imageUrl.includes(searcher)) {
                image = imageUrl;
                return true;
            } else return false;
        });
        if (image)
            image = normalizeUrl(image, url);
    }

    return image;
}

var getOgImage = (html, url) => {
    var image = html('meta[property="og:image"]');
    return image ? image.attr('content') : false;
}

var getIcon = (html, url) => {
    var image = html('link[rel="shortcut icon"]').attr('href');
    if (!image) image = url + 'favicon.ico';
}

var updateImage = (key, image) => {
    var logoElem = window.document.querySelector('#feed-logo-' + key + ' img');
    if (logoElem) {
        logoElem.src = image;
    }
}

var getLogo = async.queue( (el, next) => {

    if (el.feed) var url = el.feed;
    else if (el.search && el.search.searcher) var url = el.search.searcher;
    else if (el.match) var url = el.match.split('\\/').join('/').split('\\.').join('.').split('s?:').join(':');

    if (url) {

        if (ls('pluginLogos')[url] && ls('pluginLogos')[url] != 'https://s.ytimg.com/yts/img/avatar_720-vflYJnzBZ.png') {
            updateImage(el.key, ls('pluginLogos')[url]);
            next();
        } else {

            var origUrl = url;

            if (!el.isSubPage) url = url.substr(0, url.split('/', 3).join('/').length + 1);

            if (el.categories) {
                for (var firstCat in el.categories) break;
                url = url.replace('%c', el.categories[firstCat]);
            }

            var client = new MetaInspector(url, {
                timeout: 10000
            });

            client.on("fetch", function() {
                var image = false;
                if (el.useBackupImage) {
                    image = el.backupImage;
                } else if (el.imageType) {
                    if (el.imageType == 'ogImage') {
                        image = getOgImage(client.parsedDocument, url);
                        if (image && url.includes('youtube.com')) image = image.replace('s900', 's100');
                        if (!image && el.backupImage) image = el.backupImage;
                    } else if (el.imageType == 'firstImage') {
                        image = scanHtml(client.parsedDocument, url, 'img', 'src', '');

                        if (image && url.includes('reddit.com') && image.includes('https://www.redditstatic.com/'))
                            image = false;

                        if (!image && el.backupImage) image = el.backupImage;
                    }
                }

                if (!image && client.image) image = client.image;
                if (!image) {
                    // search for logo
                    image = scanHtml(client.parsedDocument, url, 'img', 'src', 'logo');
                    if (!image)
                        image = scanHtml(client.parsedDocument, url, 'meta', 'content', '.png');
                }
                if (!image) image = getIcon(client.parsedDocument, url);
                if (image) {
                    image = normalizeUrl(image, url);
                    var pluginLogos = ls('pluginLogos');
                    pluginLogos[origUrl] = image;
                    ls('pluginLogos', pluginLogos);
                    updateImage(el.key, image);
                }

                next();
            });

            client.on("error", () => {
                next();
            });

            client.fetch();
        }
    } else next();

}, 1);

export
default React.createClass({

    getInitialState() {
        return {
            open: true,
            selected: false
        }
    },

    decideUpdate(results) {
        if (results) {
            this.setSearch(results);
        } else if (this.state.selected && this.state.selected.label == 'Installed') {
            skipScroll = true;
            this.setSelected(this.state.selected);
        }
    },

    componentWillMount() {
        plugins.events.on('pluginListUpdate', this.decideUpdate);
    },

    componentWillUnmount() {
        plugins.events.removeListener('pluginListUpdate', this.decideUpdate);
    },
    componentDidUpdate() {
        if (!skipScroll)
            document.querySelector('.plugin-list').scrollTop = 0;
        else skipScroll = false;
    },

    setSearch(el) {
        this.setState({
            search: el
        });
    },
    setSelected(el) {
        if (el) {
            if (el.label == 'Search') {
                ModalActions.searchPlugins();
            } else {
                if (el.matchWith)
                    this.setState({
                        matchWith: el
                    });
                else
                    this.setState({
                        selected: el
                    });
            }
        } else {
            if (this.state.matchWith)
                this.setState({
                    matchWith: null
                });
            else {
                if (this.state.search)
                    this.setState({
                        search: null
                    });
                else
                    this.setState({
                        selected: null
                    });
            }
        }
    },

    openPluginModal(feed, ij) {
        var targetPlugin = feed[ij];
        var logoElem = window.document.querySelector('#feed-logo-' + ij);
        if (logoElem && logoElem.style.background) {
            targetPlugin.image = logoElem.style.background.substr(4);
            targetPlugin.image = targetPlugin.image.substr(0,targetPlugin.image.indexOf(')'));
            targetPlugin.image = targetPlugin.image.split('"').join('').split("'").join('');
        }
        if (targetPlugin.tags.indexOf('installed') > -1)
            ModalActions.installedPlugin(targetPlugin);
        else
            ModalActions.plugin(targetPlugin);
    },

    render() {
        if (!this.state.selected && !this.state.search) {
            var title = 'Select Interest';
            var feed = [
                {
                    label: 'Search',
                    icon: 'search'
                },
                {
                    label: 'Installed',
                    icon: 'installed',
                    tag: ['installed']
                },
                {
                    label: 'All',
                    icon: 'all'
                },
                {
                    label: 'Trending',
                    icon: 'hot',
                    tag: ['hot']
                },
                {
                    label: 'Interesting',
                    icon: 'idea',
                    tag: ['interesting']
                },
                {
                    label: 'Information',
                    icon: 'educational',
                    tag: ['educational']
                },
                {
                    label: 'Comedy',
                    icon: 'funny',
                    tag: ['funny']
                },
                {
                    label: 'News',
                    icon: 'news',
                    tag: ['news']
                },
                {
                    label: 'Movies',
                    icon: 'movies',
                    tag: ['movies']
                },
                {
                    label: 'TV Shows',
                    icon: 'tv-shows',
                    tag: ['tv shows']
                },
                {
                    label: 'Music',
                    icon: 'music',
                    tag: ['music']
                },
                {
                    label: 'Torrents',
                    icon: 'torrent',
                    tag: ['torrents']
                },
                {
                    label: 'Science',
                    icon: 'science',
                    tag: ['science']
                },
                {
                    label: 'Games',
                    icon: 'games',
                    tag: ['games']
                },
                {
                    label: 'Sports',
                    icon: 'sports',
                    tag: ['sports']
                },
                {
                    label: 'Hardware / Tech',
                    icon: 'tech',
                    tag: ['hardware', 'tech']
                },
                {
                    label: 'Food',
                    icon: 'food',
                    tag: ['food']
                },
                {
                    label: 'Adventure / Travel',
                    icon: 'travel',
                    tag: ['adventure', 'travel']
                },
                {
                    label: 'Beauty',
                    icon: 'beauty',
                    tag: ['beauty', 'makeup', 'fashion']
                },
                {
                    label: 'People',
                    icon: 'people',
                    tag: ['people', 'celebrities']
                },
                {
                    label: 'Children',
                    icon: 'children',
                    tag: ['children', 'cartoons']
                },
                {
                    label: 'Miscellaneous',
                    icon: 'misc',
                    tag: ['various']
                }

            ];

            if (!ls('torrentContent'))
                feed = _.filter(feed, el => { return el.icon != 'torrent' })

            if (ls('adultContent'))
                feed.push({
                    label: 'Porn',
                    icon: 'porn',
                    tag: ['porn']
                });

            var generated = _.map(feed, (el, ij) => {
                return (

                          <paper-icon-button className={'feed-interest'} src={'images/icons/discover-icons/' + el.icon + '.png'} noink={true} style={{width: '60px', height: '60px', padding: '0', marginBottom: '7px' }} />

                        {el.label}

                );
            });

            var tagTemplate = '';

            var backButton = '';

        } else {
            var title = this.state.search ? this.state.search.terms : this.state.matchWith ? this.state.selected.label +'  -  '+ this.state.matchWith.label : this.state.selected.label;
            var feed = this.state.search ? this.state.search.results : plugins.matchTags(this.state.selected.tag);

            if (!ls('torrentContent'))
                feed = _.filter(feed, el => { return !el.torrent })

            var tagMap =[];
            var tags = [];

            if (this.state.matchWith) {
                var noTags = true;
                feed.forEach( el => {
                    el.tags.forEach( elm => {
                        var index = tagMap.indexOf(elm);
                        if (index > -1) tags[index].votes++;
                        else {
                            tags.push({ tag: elm, votes: 1 });
                            tagMap.push(elm);
                        }
                    });
                });
                feed = plugins.matchWith(feed, this.state.matchWith.tag);
            }

            getLogo.tasks = [];

            // order by installed first, then by priority
            feed.sort(function(a,b) { return (a.tags.indexOf('installed') > -1 && b.tags.indexOf('installed') == -1) ? -1 : ((a.tags.indexOf('installed') == -1 && b.tags.indexOf('installed') > -1) ? 1 : ((a.priority > b.priority) ? -1 : ((b.priority > a.priority) ? 1 : 0))); });

            var generated = _.map(feed, (el, ij) => {
                el.key = ij;
                var logoImg
                if (el.defaultImage) {
                    logoImg = el.defaultImage
                } else {
                    getLogo.push(el);
                }

                if (!logoImg)
                    logoImg = 'images/plugin-placeholder.png'

                if (!noTags)
                    el.tags.forEach( elm => {
                        var index = tagMap.indexOf(elm);
                        if (index > -1) tags[index].votes++;
                        else {
                            tags.push({ tag: elm, votes: 1 });
                            tagMap.push(elm);
                        }
                    });

                return (

                            {e.target.onerror = null; e.target.src="images/plugin-placeholder.png"}} />

{el.name}

                );
            });

            tags.sort(function(a,b) { return (a.votes > b.votes) ? -1 : ((b.votes > a.votes) ? 1 : 0); });

            tags = tags.slice(0, 12);

            var tagTemplate = _.map(tags, (el, ij) => {
                return (

                            {el.tag} ({el.votes})

                );
            });

            var backButton = (
                <paper-icon-button onClick={this.setSelected.bind(this, null)} className="player-close" icon={'arrow-back'} noink={true} style={{color: '#767A7B', position: 'relative', top: '-2px', marginRight: '1px', marginLeft: '-20px'}} />
            );
        }
        return (

                {backButton}
                {title}

                {tagTemplate}

                    {generated}

        );
    }
});

<commit_msg>Delete Torrent Plugin Category<commit_after>import React from 'react';
import _ from 'lodash';
import plugins from '../../../utils/plugins';
import async from 'async';
import MetaInspector from 'node-metainspector';
import ls from 'local-storage';
import ModalActions from '../../Modal/actions';

var skipScroll;

var titleCase = str => {
   var splitStr = str.toLowerCase().split(' ');
   for (var i = 0; i < splitStr.length; i++) {
       splitStr[i] = splitStr[i].charAt(0).toUpperCase() + splitStr[i].substring(1);
   }
   return splitStr.join(' ');
}

var normalizeUrl = (image, url) => {
    var protocol = url.substr(0, url.indexOf(':'));
    if (image.startsWith('//')) image = protocol + ':' + image;
    if (image.startsWith('/')) image = url + image.substr(1);
    return image;
}

var scanHtml = (html, url, target, attr, searcher) => {
    var image = false;
    var imgs = html(target);
    if (imgs && imgs.length) {
        _.some( imgs, el => {
            var imageUrl = html(el).attr(attr);
            if (imageUrl && imageUrl.includes(searcher)) {
                image = imageUrl;
                return true;
            } else return false;
        });
        if (image)
            image = normalizeUrl(image, url);
    }

    return image;
}

var getOgImage = (html, url) => {
    var image = html('meta[property="og:image"]');
    return image ? image.attr('content') : false;
}

var getIcon = (html, url) => {
    var image = html('link[rel="shortcut icon"]').attr('href');
    if (!image) image = url + 'favicon.ico';
}

var updateImage = (key, image) => {
    var logoElem = window.document.querySelector('#feed-logo-' + key + ' img');
    if (logoElem) {
        logoElem.src = image;
    }
}

var getLogo = async.queue( (el, next) => {

    if (el.feed) var url = el.feed;
    else if (el.search && el.search.searcher) var url = el.search.searcher;
    else if (el.match) var url = el.match.split('\\/').join('/').split('\\.').join('.').split('s?:').join(':');

    if (url) {

        if (ls('pluginLogos')[url] && ls('pluginLogos')[url] != 'https://s.ytimg.com/yts/img/avatar_720-vflYJnzBZ.png') {
            updateImage(el.key, ls('pluginLogos')[url]);
            next();
        } else {

            var origUrl = url;

            if (!el.isSubPage) url = url.substr(0, url.split('/', 3).join('/').length + 1);

            if (el.categories) {
                for (var firstCat in el.categories) break;
                url = url.replace('%c', el.categories[firstCat]);
            }

            var client = new MetaInspector(url, {
                timeout: 10000
            });

            client.on("fetch", function() {
                var image = false;
                if (el.useBackupImage) {
                    image = el.backupImage;
                } else if (el.imageType) {
                    if (el.imageType == 'ogImage') {
                        image = getOgImage(client.parsedDocument, url);
                        if (image && url.includes('youtube.com')) image = image.replace('s900', 's100');
                        if (!image && el.backupImage) image = el.backupImage;
                    } else if (el.imageType == 'firstImage') {
                        image = scanHtml(client.parsedDocument, url, 'img', 'src', '');

                        if (image && url.includes('reddit.com') && image.includes('https://www.redditstatic.com/'))
                            image = false;

                        if (!image && el.backupImage) image = el.backupImage;
                    }
                }

                if (!image && client.image) image = client.image;
                if (!image) {
                    // search for logo
                    image = scanHtml(client.parsedDocument, url, 'img', 'src', 'logo');
                    if (!image)
                        image = scanHtml(client.parsedDocument, url, 'meta', 'content', '.png');
                }
                if (!image) image = getIcon(client.parsedDocument, url);
                if (image) {
                    image = normalizeUrl(image, url);
                    var pluginLogos = ls('pluginLogos');
                    pluginLogos[origUrl] = image;
                    ls('pluginLogos', pluginLogos);
                    updateImage(el.key, image);
                }

                next();
            });

            client.on("error", () => {
                next();
            });

            client.fetch();
        }
    } else next();

}, 1);

export
default React.createClass({

    getInitialState() {
        return {
            open: true,
            selected: false
        }
    },

    decideUpdate(results) {
        if (results) {
            this.setSearch(results);
        } else if (this.state.selected && this.state.selected.label == 'Installed') {
            skipScroll = true;
            this.setSelected(this.state.selected);
        }
    },

    componentWillMount() {
        plugins.events.on('pluginListUpdate', this.decideUpdate);
    },

    componentWillUnmount() {
        plugins.events.removeListener('pluginListUpdate', this.decideUpdate);
    },
    componentDidUpdate() {
        if (!skipScroll)
            document.querySelector('.plugin-list').scrollTop = 0;
        else skipScroll = false;
    },

    setSearch(el) {
        this.setState({
            search: el
        });
    },
    setSelected(el) {
        if (el) {
            if (el.label == 'Search') {
                ModalActions.searchPlugins();
            } else {
                if (el.matchWith)
                    this.setState({
                        matchWith: el
                    });
                else
                    this.setState({
                        selected: el
                    });
            }
        } else {
            if (this.state.matchWith)
                this.setState({
                    matchWith: null
                });
            else {
                if (this.state.search)
                    this.setState({
                        search: null
                    });
                else
                    this.setState({
                        selected: null
                    });
            }
        }
    },

    openPluginModal(feed, ij) {
        var targetPlugin = feed[ij];
        var logoElem = window.document.querySelector('#feed-logo-' + ij);
        if (logoElem && logoElem.style.background) {
            targetPlugin.image = logoElem.style.background.substr(4);
            targetPlugin.image = targetPlugin.image.substr(0,targetPlugin.image.indexOf(')'));
            targetPlugin.image = targetPlugin.image.split('"').join('').split("'").join('');
        }
        if (targetPlugin.tags.indexOf('installed') > -1)
            ModalActions.installedPlugin(targetPlugin);
        else
            ModalActions.plugin(targetPlugin);
    },

    render() {
        if (!this.state.selected && !this.state.search) {
            var title = 'Select Interest';
            var feed = [
                {
                    label: 'Search',
                    icon: 'search'
                },
                {
                    label: 'Installed',
                    icon: 'installed',
                    tag: ['installed']
                },
                {
                    label: 'All',
                    icon: 'all'
                },
                {
                    label: 'Trending',
                    icon: 'hot',
                    tag: ['hot']
                },
                {
                    label: 'Interesting',
                    icon: 'idea',
                    tag: ['interesting']
                },
                {
                    label: 'Information',
                    icon: 'educational',
                    tag: ['educational']
                },
                {
                    label: 'Comedy',
                    icon: 'funny',
                    tag: ['funny']
                },
                {
                    label: 'News',
                    icon: 'news',
                    tag: ['news']
                },
                {
                    label: 'Movies',
                    icon: 'movies',
                    tag: ['movies']
                },
                {
                    label: 'TV Shows',
                    icon: 'tv-shows',
                    tag: ['tv shows']
                },
                {
                    label: 'Music',
                    icon: 'music',
                    tag: ['music']
                },
                {
                    label: 'Science',
                    icon: 'science',
                    tag: ['science']
                },
                {
                    label: 'Games',
                    icon: 'games',
                    tag: ['games']
                },
                {
                    label: 'Sports',
                    icon: 'sports',
                    tag: ['sports']
                },
                {
                    label: 'Hardware / Tech',
                    icon: 'tech',
                    tag: ['hardware', 'tech']
                },
                {
                    label: 'Food',
                    icon: 'food',
                    tag: ['food']
                },
                {
                    label: 'Adventure / Travel',
                    icon: 'travel',
                    tag: ['adventure', 'travel']
                },
                {
                    label: 'Beauty',
                    icon: 'beauty',
                    tag: ['beauty', 'makeup', 'fashion']
                },
                {
                    label: 'People',
                    icon: 'people',
                    tag: ['people', 'celebrities']
                },
                {
                    label: 'Children',
                    icon: 'children',
                    tag: ['children', 'cartoons']
                },
                {
                    label: 'Miscellaneous',
                    icon: 'misc',
                    tag: ['various']
                }

            ];

            if (!ls('torrentContent'))
                feed = _.filter(feed, el => { return el.icon != 'torrent' })

            if (ls('adultContent'))
                feed.push({
                    label: 'Porn',
                    icon: 'porn',
                    tag: ['porn']
                });

            var generated = _.map(feed, (el, ij) => {
                return (

                          <paper-icon-button className={'feed-interest'} src={'images/icons/discover-icons/' + el.icon + '.png'} noink={true} style={{width: '60px', height: '60px', padding: '0', marginBottom: '7px' }} />

                        {el.label}

                );
            });

            var tagTemplate = '';

            var backButton = '';

        } else {
            var title = this.state.search ? this.state.search.terms : this.state.matchWith ? this.state.selected.label +'  -  '+ this.state.matchWith.label : this.state.selected.label;
            var feed = this.state.search ? this.state.search.results : plugins.matchTags(this.state.selected.tag);

            if (!ls('torrentContent'))
                feed = _.filter(feed, el => { return !el.torrent })

            var tagMap =[];
            var tags = [];

            if (this.state.matchWith) {
                var noTags = true;
                feed.forEach( el => {
                    el.tags.forEach( elm => {
                        var index = tagMap.indexOf(elm);
                        if (index > -1) tags[index].votes++;
                        else {
                            tags.push({ tag: elm, votes: 1 });
                            tagMap.push(elm);
                        }
                    });
                });
                feed = plugins.matchWith(feed, this.state.matchWith.tag);
            }

            getLogo.tasks = [];

            // order by installed first, then by priority
            feed.sort(function(a,b) { return (a.tags.indexOf('installed') > -1 && b.tags.indexOf('installed') == -1) ? -1 : ((a.tags.indexOf('installed') == -1 && b.tags.indexOf('installed') > -1) ? 1 : ((a.priority > b.priority) ? -1 : ((b.priority > a.priority) ? 1 : 0))); });

            var generated = _.map(feed, (el, ij) => {
                el.key = ij;
                var logoImg
                if (el.defaultImage) {
                    logoImg = el.defaultImage
                } else {
                    getLogo.push(el);
                }

                if (!logoImg)
                    logoImg = 'images/plugin-placeholder.png'

                if (!noTags)
                    el.tags.forEach( elm => {
                        var index = tagMap.indexOf(elm);
                        if (index > -1) tags[index].votes++;
                        else {
                            tags.push({ tag: elm, votes: 1 });
                            tagMap.push(elm);
                        }
                    });

                return (

                            {e.target.onerror = null; e.target.src="images/plugin-placeholder.png"}} />

{el.name}

                );
            });

            tags.sort(function(a,b) { return (a.votes > b.votes) ? -1 : ((b.votes > a.votes) ? 1 : 0); });

            tags = tags.slice(0, 12);

            var tagTemplate = _.map(tags, (el, ij) => {
                return (

                            {el.tag} ({el.votes})

                );
            });

            var backButton = (
                <paper-icon-button onClick={this.setSelected.bind(this, null)} className="player-close" icon={'arrow-back'} noink={true} style={{color: '#767A7B', position: 'relative', top: '-2px', marginRight: '1px', marginLeft: '-20px'}} />
            );
        }
        return (

                {backButton}
                {title}

                {tagTemplate}

                    {generated}

        );
    }
});

<|endoftext|>