<commit_before>/**
* map tab shown in cartodb admin
*/

cdb.admin.MapTab = cdb.core.View.extend({

  events: {
    'click .clearview': '_clearView',
    'click .sqlview .export_query': '_tableFromQuery'
  },

  className: 'map',
  animation_time: 300,

  initialize: function() {
    this.template = this.getTemplate('table/views/maptab');
    this.map = this.model;
    this.map_enabled = false;
    this.featureHovered = null;
    this.georeferenced = false;
    this.activeLayerView = null;

    this.add_related_model(this.map);

    var self = this;

    // Actions triggered in the right panel
    cdb.god.bind("panel_action", function(action) {
      self._moveInfo(action);
    }, this);

    this.add_related_model(cdb.god);

    this.map.bind('change:provider', this.switchMapType, this);

    _.bindAll(this, 'showNoGeoRefWarning');
  },

  deactivated: function() {
    if(this.map_enabled) {
      this.clearMap();
    }
  },

  clearMap: function() {
    clearTimeout(this.autoSaveBoundsTimer);
    this.mapView.clean();
    this.basemapDropdown.clean();
    this.zoom.clean();
    if(this.infowindow) {
      this.infowindow.clean();
    }
    if(this.geometryEditor) this.geometryEditor.clean();
    if(this.table) {
      this.table.unbind(null, null, this);
    }

    delete this.mapView;
    delete this.basemapDropdown;
    delete this.zoom;
    delete this.infowindow;
    delete this.geometryEditor;

    this.map_enabled = false;

    // place the map DOM object again
    this.render();
  },

  /**
   *  Hide the infowindow when a query is applied or cleared
   */
  _hideInfowindow: function() {
    if(this.infowindow) {
      this.infowindow.model.set('visibility', false);
    }
  },

  /**
  * this function is used when the map library is changed. Each map library
  * works in different way and need to recreate all the components again
  */
  switchMapType: function() {
    if (this.map_enabled) {
      this.clearMap();
      this.render();
      this.enableMap();
    }
  },

  /**
  * map can't be loaded from the beggining, it needs the DOM to be loaded
  * so we wait until is actually shown to create the mapview and show it
  */
  enableMap: function() {

    this.$('.tipsy').remove();
    var self = this;

    if (!this.map_enabled) {

      var div = this.$('.cartodb-map');

      var mapViewClass = cdb.geo.LeafletMapView;

      if(this.map.get('provider') === 'googlemaps') {
        mapViewClass = cdb.geo.GoogleMapsMapView;
      }

      this.mapView = new mapViewClass({
        el: div,
        map: this.map
      });

      this.clickTimeout = null;
      this._bindMissingClickEvents();

      this.map_enabled = true;

      //this.mapView.bind('newLayerView', self._bindDataLayer, self);

      // bind data layer if it is already added to the map
      /*var dataLayer = this.map.get('dataLayer');
      if(dataLayer) {
        var dl = this.mapView.getLayerByCid(self.map.get('dataLayer').cid);
        this._bindDataLayer(dl);
      }*/

      // Create Base Layer chooser
      if (!this.basemapDropdown) {

        this.basemapDropdown = new cdb.admin.DropdownBasemap({
          target: $('.basemap_dropdown'),
          position: "position",
          template_base: "common/views/dropdown_basemap",
          model: this.map,
          mapview: this.mapView,
          baseLayers: this.options.baseLayers,
          tick: "left",
          horizontal_position: "left",
          horizontal_offset: "40px"
        });

        this.addView(this.basemapDropdown);

        cdb.god.bind("closeDialogs", this.basemapDropdown.hide, this.basemapDropdown);

        $(".basemap_dropdown").append(this.basemapDropdown.render().el);
      }

      // Set active base layer if it already exists
      if (this.map.getBaseLayer()) {
        this.basemapDropdown.setActiveBaselayer();
      }

      // Zoom control
      var zoomControl = new cdb.geo.ui.Zoom({
        model:    this.map,
        template: cdb.templates.getTemplate("table/views/zoom_control")
      });

      this.$el.append(zoomControl.render().$el);
      this.zoom = zoomControl;

      // Search control
      var searchControl = new cdb.geo.ui.Search({
        model:    this.map,
        template: cdb.templates.getTemplate("table/views/search_control")
      });

      this.$el.append(searchControl.render().$el);
      this.search = searchControl;

      // Zoom info
      var zoomInfo = this.zoomInfo = new cdb.geo.ui.ZoomInfo({
        model: this.map
      });

      this.$el.append(zoomInfo.render().$el);
      // Create tipsy and set to the zoom info element
      zoomInfo.$el.tipsy({
        title: function() { return "Zoom level" },
        fade: true,
        offset: 3,
        gravity: 'w'
      });

      // Tiles loader
      var tilesLoader = this.loader = new cdb.geo.ui.TilesLoader({
        template: cdb.templates.getTemplate("table/views/tiles_loader")
      });

      this.$el.append(tilesLoader.render().$el);
      // Create tipsy and set to the tiles loader
      tilesLoader.$el.find("div.loader").tipsy({
        title: function() { return "Loading tiles..." },
        fade: true,
        offset: 3,
        gravity: 'w'
      });

      this._addInfowindow();

      this.mapView.bind('newLayerView', function(layerView) {
        if(this.activeLayerView && layerView.model.cid == this.activeLayerView.model.cid) {
          this._bindDataLayer(this.activeLayerView);
        }
      }, this);

      if(this.activeLayerView) {
        this._bindDataLayer(this.activeLayerView);
      }

      // HACK
      // wait a little bit to give time to the mapview
      // to estabilize
      this.autoSaveBoundsTimer = setTimeout(function() {
        self.mapView.setAutoSaveBounds();
      }, 1000);
    }
  },

  bindGeoRefCheck: function() {
    if(!this.table.data().fetched) {
      this.table.bind('dataLoaded', function() {
        this.checkGeoRef();
      }, this);
    } else {
      this.checkGeoRef();
    }
  },

  activated: function() {
    $(window).scrollTop(0);
  },

  checkGeoRef: function() {
    if(this.options && this.table) {
      if(!this.table.isGeoreferenced()) {
        this.trigger('noGeoRef');
        this.showNoGeoRefWarning();
        this.georeferenced = false;
      } else {
        if(!this.georeferenced) {
          this.trigger('geoRef');
        }
        this.georeferenced = true;
        if(this.noGeoRefDialog) {
          this.noGeoRefDialog.hide();
        }
      }
    }
  },

  /**
  * this function binds click and dblclick events
  * in order to not raise click when user does a dblclick
  *
  * it raises a missingClick when the user clicks on the map
  * but not over a feature or ui component
  */
  _bindMissingClickEvents: function() {
    var self = this;
    this.mapView.bind('click', function(e) {
      if(self.clickTimeout === null) {
        self.clickTimeout = setTimeout(function() {
          self.clickTimeout = null;
          if(!self.featureHovered) {
            self.trigger('missingClick');
          }
        }, 150);
      }
      //google maps does not send an event
      if(!self.featureHovered && e.preventDefault) {
        e.preventDefault();
        e.stopPropagation();
      }
    });

    this.mapView.bind('dblclick', function() {
      if(self.clickTimeout !== null) {
        clearTimeout(self.clickTimeout);
        self.clickTimeout = null;
      }
    });
  },

  setActiveLayer: function(layerView) {
    this.activeLayerView = layerView;
    // check if the map is rendered and the layer is in the map
    if(this.mapView && this.mapView.getLayerByCid(layerView.model.cid)) {
      this._bindDataLayer(layerView);
    }
  },

  /**
  * when the layer view is created this method is called
  * to attach all the click events
  */
  _bindDataLayer: function(layerView) {
    var self = this;
    var layer = layerView.model;
    var layerType = layer.get('type');

    if (layerType === 'CartoDB') { // unbind previos stuff

      // Set data layer bindings
      if (self.layerDataView) {
        self.layerDataView.unbind(null, null, this);
      }
      self.infowindowModel = layer.infowindow;
      self._bindTable(layer.table);
      self.layerDataView = self.mapView.getLayerByCid(layer.cid);

      if (self.layerDataView) {
        self.layerDataView.bind('featureClick', self.featureClick, self);
        self.layerDataView.bind('featureOut',   self.featureOut,   self);
        self.layerDataView.bind('featureOver',  self.featureOver,  self);
        self.layerDataView.bind('loading',      self.loadingTiles, self);
        self.layerDataView.bind('load',         self.loadTiles,    self);
      }

      // Set layer model binding
      if (layerView && layerView.model) {
        layerView && layerView.model.unbind('startEdition',this._addGeometry, this);
        layerView.model.bind('startEdition', this._addGeometry, this);
      }
    }
  },

  _addInfowindow: function() {
    if(this.infowindow) this.infowindow.clean();
    if(this.table && this.mapView) {
      this.infowindow = new cdb.admin.MapInfowindow({
        model: this.infowindowModel,
        mapView: this.mapView,
        table: this.table
      });
      this.mapView.$el.append(this.infowindow.el);

      // Editing geometry
      if(this.geometryEditor) this.geometryEditor.clean();

      this.geometryEditor = new cdb.admin.GeometryEditor({
        model: this.table
      });

      this.geometryEditor.mapView = this.mapView;
      this.mapView.$el.append(this.geometryEditor.render().el);
      this.geometryEditor.hide();

      this.geometryEditor.bind('editStart', this.hideDataLayer, this);
      this.geometryEditor.bind('editDiscard', this.showDataLayer, this);
      this.geometryEditor.bind('editFinish', this.showDataLayer, this);
      this.geometryEditor.bind('editFinish', this.updateDataLayerView, this);
      this.geometryEditor.bind('geomCreated', function(row) {
        this.table.data().add(row);
      }, this);

      this.infowindow.bind('editGeom', this.geometryEditor.editGeom, this.geometryEditor);
      this.table.bind('remove:row', this.updateDataLayerView, this);

      this.table.bind('change:dataSource', function() {
        this.geometryEditor.discard();
      }, this);
    }
  },

  _addGeometry: function(type) {
    this.geometryEditor.createGeom(this.table.data().newRow(), type);
  },

  _bindTable: function(table) {

    if(this.table) {
      this.table.unbind(null, null, this);
    }
    this.table = table;

    this.table.bind('change:dataSource', this._hideInfowindow, this);

    this.table.bind('data:saved', function() {
      this.updateDataLayerView();
    }, this);

    this.table.bind('change:dataSource', function() {
      if(this.table.isInSQLView()) {
        this._addSQLViewHeader();
      } else {
        this._removeSQLViewHeader();
      }
    }, this);

    this.table.bind('change:schema', function() {
      //dataLayer.invalidate();
    }, this);

    // infowindow
    this._addInfowindow();

    if(this.table.isInSQLView()) {
      this._addSQLViewHeader();
    } else {
      this._removeSQLViewHeader();
    }

    this.bindGeoRefCheck();
  },

  loadingTiles: function() {
    this.loader.show()
  },

  loadTiles: function() {
    this.loader.hide()
  },

  featureOver: function(e, latlon, pxPos, data) {
    if(this.infowindowModel.get('disabled')) return;
    this.mapView.setCursor('pointer');
    this.featureHovered = data;
  },

  featureOut: function() {
    if(this.infowindowModel.get('disabled')) return;
    this.mapView.setCursor('auto');
    this.featureHovered = null;
  },

  featureClick: function(e, latlon, pxPos, data) {
    if(this.infowindowModel.get('disabled')) return;
    if(!this.geometryEditor.isEditing()) {
      if(data.cartodb_id) {
        this.infowindow
        .setLatLng(latlon)
        .setFeatureInfo(data.cartodb_id)
        .showInfowindow();
      } else {
        cdb.log.error("can't show infowindow, no cartodb_id on data");
      }
    }
  },

  /**
   *  Move all necessary blocks when panel is openned (normal, narrowed,...) or closed
   */
  _moveInfo: function(type) {
    if (type == "show") {
      this.$el
        .removeClass('narrow')
        .addClass('displaced');
    } else if (type == "hide") {
      this.$el.removeClass('narrow displaced');
    } else if (type == "narrow") {
      this.$el.addClass('narrow displaced');
    }
  },

  render: function() {
    this.$el.html('');
    this.$el.append(this.template());
    return this;
  },

  showDataLayer: function() {
    this.layerDataView.setInteraction(true);
    this.layerDataView.setOpacity(1.0);
  },

  hideDataLayer: function() {
    //this.layerDataView.hide();
    this.layerDataView.setInteraction(false);
    this.layerDataView.setOpacity(0.5);
  },

  /**
  * reload tiles
  */
  updateDataLayerView: function() {
    if(this.layerDataView) {
      this.layerDataView.reload();
    }
  },
  /**
  * Paints a dialog with a warning when the user hasn't any georeferenced row
  * @method showNoGeorefWarning
  */
  showNoGeoRefWarning: function() {
    var warningStorageName = '';
    if(this.table.data().length > 0) {
      warningStorageName = 'georefContentWarningShowed' + this.table.id;
    } else {
      warningStorageName = 'georefNoContentWarningShowed' + this.table.id;
    }

    if(!this.noGeoRefDialog && !this.table.isInSQLView() && (!localStorage[warningStorageName])) {
      localStorage[warningStorageName] = true;
      this.noGeoRefDialog = new cdb.admin.NoGeoRefDataDialog({
        model: this.table
      });
      this.retrigger('georeference', this.noGeoRefDialog);
      this.retrigger('manual', this.noGeoRefDialog);
      this.noGeoRefDialog.render().$el.appendTo(this.$el);
    }
    // if the dialog already has been shown, we don't show it again
  },

  //adds the green indicator when a query is applied
  _addSQLViewHeader: function() {
    this.$('.sqlview').remove();
    var total = this.table.data().size();
    var html = this.getTemplate('table/views/sql_view_notice')({
      empty: !total
    });
    this.$('.cartodb-map').after(html);
  },

  _removeSQLViewHeader: function() {
    this.$('.sqlview').remove();
  },

  _clearView: function(e) {
    this.killEvent(e);
    this.trigger('clearSQLView');
    return false;
  },

  _tableFromQuery: function(e) {
    this.killEvent(e);

    var duplicate_dialog = new cdb.admin.DuplicateTable({
      model: this.table
    });
    duplicate_dialog.appendToBody().open();
  }

});

<commit_msg>when query is applied, green band in mapview appears and buttons are working<commit_after>/**
* map tab shown in cartodb admin
*/

cdb.admin.MapTab = cdb.core.View.extend({

  events: {
    'click .sqlview .clearview':    '_clearView',
    'click .sqlview .export_query': '_tableFromQuery'
  },

  className: 'map',
  animation_time: 300,

  initialize: function() {
    this.template = this.getTemplate('table/views/maptab');
    this.map = this.model;
    this.map_enabled = false;
    this.featureHovered = null;
    this.georeferenced = false;
    this.activeLayerView = null;

    this.add_related_model(this.map);

    var self = this;

    // Actions triggered in the right panel
    cdb.god.bind("panel_action", function(action) {
      self._moveInfo(action);
    }, this);

    this.add_related_model(cdb.god);

    this.map.bind('change:provider', this.switchMapType, this);

    _.bindAll(this, 'showNoGeoRefWarning');
  },

  deactivated: function() {
    if(this.map_enabled) {
      this.clearMap();
    }
  },

  clearMap: function() {
    clearTimeout(this.autoSaveBoundsTimer);
    this.mapView.clean();
    this.basemapDropdown.clean();
    this.zoom.clean();
    if(this.infowindow) {
      this.infowindow.clean();
    }
    if(this.geometryEditor) this.geometryEditor.clean();
    if(this.table) {
      this.table.unbind(null, null, this);
    }

    delete this.mapView;
    delete this.basemapDropdown;
    delete this.zoom;
    delete this.infowindow;
    delete this.geometryEditor;

    this.map_enabled = false;

    // place the map DOM object again
    this.render();
  },

  /**
   *  Hide the infowindow when a query is applied or cleared
   */
  _hideInfowindow: function() {
    if(this.infowindow) {
      this.infowindow.model.set('visibility', false);
    }
  },

  /**
  * this function is used when the map library is changed. Each map library
  * works in different way and need to recreate all the components again
  */
  switchMapType: function() {
    if (this.map_enabled) {
      this.clearMap();
      this.render();
      this.enableMap();
    }
  },

  /**
  * map can't be loaded from the beggining, it needs the DOM to be loaded
  * so we wait until is actually shown to create the mapview and show it
  */
  enableMap: function() {

    this.$('.tipsy').remove();
    var self = this;

    if (!this.map_enabled) {

      var div = this.$('.cartodb-map');

      var mapViewClass = cdb.geo.LeafletMapView;

      if(this.map.get('provider') === 'googlemaps') {
        mapViewClass = cdb.geo.GoogleMapsMapView;
      }

      this.mapView = new mapViewClass({
        el: div,
        map: this.map
      });

      this.clickTimeout = null;
      this._bindMissingClickEvents();

      this.map_enabled = true;

      //this.mapView.bind('newLayerView', self._bindDataLayer, self);

      // bind data layer if it is already added to the map
      /*var dataLayer = this.map.get('dataLayer');
      if(dataLayer) {
        var dl = this.mapView.getLayerByCid(self.map.get('dataLayer').cid);
        this._bindDataLayer(dl);
      }*/

      // Create Base Layer chooser
      if (!this.basemapDropdown) {

        this.basemapDropdown = new cdb.admin.DropdownBasemap({
          target: $('.basemap_dropdown'),
          position: "position",
          template_base: "common/views/dropdown_basemap",
          model: this.map,
          mapview: this.mapView,
          baseLayers: this.options.baseLayers,
          tick: "left",
          horizontal_position: "left",
          horizontal_offset: "40px"
        });

        this.addView(this.basemapDropdown);

        cdb.god.bind("closeDialogs", this.basemapDropdown.hide, this.basemapDropdown);

        $(".basemap_dropdown").append(this.basemapDropdown.render().el);
      }

      // Set active base layer if it already exists
      if (this.map.getBaseLayer()) {
        this.basemapDropdown.setActiveBaselayer();
      }

      // Zoom control
      var zoomControl = new cdb.geo.ui.Zoom({
        model:    this.map,
        template: cdb.templates.getTemplate("table/views/zoom_control")
      });

      this.$el.append(zoomControl.render().$el);
      this.zoom = zoomControl;

      // Search control
      var searchControl = new cdb.geo.ui.Search({
        model:    this.map,
        template: cdb.templates.getTemplate("table/views/search_control")
      });

      this.$el.append(searchControl.render().$el);
      this.search = searchControl;

      // Zoom info
      var zoomInfo = this.zoomInfo = new cdb.geo.ui.ZoomInfo({
        model: this.map
      });

      this.$el.append(zoomInfo.render().$el);
      // Create tipsy and set to the zoom info element
      zoomInfo.$el.tipsy({
        title: function() { return "Zoom level" },
        fade: true,
        offset: 3,
        gravity: 'w'
      });

      // Tiles loader
      var tilesLoader = this.loader = new cdb.geo.ui.TilesLoader({
        template: cdb.templates.getTemplate("table/views/tiles_loader")
      });

      this.$el.append(tilesLoader.render().$el);
      // Create tipsy and set to the tiles loader
      tilesLoader.$el.find("div.loader").tipsy({
        title: function() { return "Loading tiles..." },
        fade: true,
        offset: 3,
        gravity: 'w'
      });

      this._addInfowindow();

      this.mapView.bind('newLayerView', function(layerView) {
        if(this.activeLayerView && layerView.model.cid == this.activeLayerView.model.cid) {
          this._bindDataLayer(this.activeLayerView);
        }
      }, this);

      if(this.activeLayerView) {
        this._bindDataLayer(this.activeLayerView);
      }

      // HACK
      // wait a little bit to give time to the mapview
      // to estabilize
      this.autoSaveBoundsTimer = setTimeout(function() {
        self.mapView.setAutoSaveBounds();
      }, 1000);
    }
  },

  bindGeoRefCheck: function() {
    if(!this.table.data().fetched) {
      this.table.bind('dataLoaded', function() {
        this.checkGeoRef();
      }, this);
    } else {
      this.checkGeoRef();
    }
  },

  activated: function() {
    $(window).scrollTop(0);
  },

  checkGeoRef: function() {
    if(this.options && this.table) {
      if(!this.table.isGeoreferenced()) {
        this.trigger('noGeoRef');
        this.showNoGeoRefWarning();
        this.georeferenced = false;
      } else {
        if(!this.georeferenced) {
          this.trigger('geoRef');
        }
        this.georeferenced = true;
        if(this.noGeoRefDialog) {
          this.noGeoRefDialog.hide();
        }
      }
    }
  },

  /**
  * this function binds click and dblclick events
  * in order to not raise click when user does a dblclick
  *
  * it raises a missingClick when the user clicks on the map
  * but not over a feature or ui component
  */
  _bindMissingClickEvents: function() {
    var self = this;
    this.mapView.bind('click', function(e) {
      if(self.clickTimeout === null) {
        self.clickTimeout = setTimeout(function() {
          self.clickTimeout = null;
          if(!self.featureHovered) {
            self.trigger('missingClick');
          }
        }, 150);
      }
      //google maps does not send an event
      if(!self.featureHovered && e.preventDefault) {
        e.preventDefault();
        e.stopPropagation();
      }
    });

    this.mapView.bind('dblclick', function() {
      if(self.clickTimeout !== null) {
        clearTimeout(self.clickTimeout);
        self.clickTimeout = null;
      }
    });
  },

  setActiveLayer: function(layerView) {
    this.activeLayerView = layerView;
    // check if the map is rendered and the layer is in the map
    if(this.mapView && this.mapView.getLayerByCid(layerView.model.cid)) {
      this._bindDataLayer(layerView);
    }
  },

  /**
  * when the layer view is created this method is called
  * to attach all the click events
  */
  _bindDataLayer: function(layerView) {
    var self = this;
    var layer = layerView.model;
    var layerType = layer.get('type');

    if (layerType === 'CartoDB') { // unbind previos stuff

      // Set data layer bindings
      if (self.layerDataView) {
        self.layerDataView.unbind(null, null, this);
      }
      self.infowindowModel = layer.infowindow;
      self._bindTable(layer.table);
      self.layerDataView = self.mapView.getLayerByCid(layer.cid);

      if (self.layerDataView) {
        self.layerDataView.bind('featureClick', self.featureClick, self);
        self.layerDataView.bind('featureOut',   self.featureOut,   self);
        self.layerDataView.bind('featureOver',  self.featureOver,  self);
        self.layerDataView.bind('loading',      self.loadingTiles, self);
        self.layerDataView.bind('load',         self.loadTiles,    self);
      }

      // Set layer model binding
      if (layerView && layerView.model) {
        layerView && layerView.model.unbind('startEdition',this._addGeometry, this);
        layerView.model.bind('startEdition', this._addGeometry, this);
      }
    }
  },

  _addInfowindow: function() {
    if(this.infowindow) this.infowindow.clean();
    if(this.table && this.mapView) {
      this.infowindow = new cdb.admin.MapInfowindow({
        model: this.infowindowModel,
        mapView: this.mapView,
        table: this.table
      });
      this.mapView.$el.append(this.infowindow.el);

      // Editing geometry
      if(this.geometryEditor) this.geometryEditor.clean();

      this.geometryEditor = new cdb.admin.GeometryEditor({
        model: this.table
      });

      this.geometryEditor.mapView = this.mapView;
      this.mapView.$el.append(this.geometryEditor.render().el);
      this.geometryEditor.hide();

      this.geometryEditor.bind('editStart', this.hideDataLayer, this);
      this.geometryEditor.bind('editDiscard', this.showDataLayer, this);
      this.geometryEditor.bind('editFinish', this.showDataLayer, this);
      this.geometryEditor.bind('editFinish', this.updateDataLayerView, this);
      this.geometryEditor.bind('geomCreated', function(row) {
        this.table.data().add(row);
      }, this);

      this.infowindow.bind('editGeom', this.geometryEditor.editGeom, this.geometryEditor);
      this.table.bind('remove:row', this.updateDataLayerView, this);

      this.table.bind('change:dataSource', function() {
        this.geometryEditor.discard();
      }, this);
    }
  },

  _addGeometry: function(type) {
    this.geometryEditor.createGeom(this.table.data().newRow(), type);
  },

  _bindTable: function(table) {

    if(this.table) {
      this.table.unbind(null, null, this);
    }
    this.table = table;

    this.table.bind('change:dataSource', this._hideInfowindow, this);

    this.table.bind('data:saved', function() {
      this.updateDataLayerView();
    }, this);

    this.table.bind('change:dataSource', function() {
      if(this.table.isInSQLView()) {
        this._addSQLViewHeader();
      } else {
        this._removeSQLViewHeader();
      }
    }, this);

    this.table.bind('change:schema', function() {
      //dataLayer.invalidate();
    }, this);

    // infowindow
    this._addInfowindow();

    if(this.table.isInSQLView()) {
      this._addSQLViewHeader();
    } else {
      this._removeSQLViewHeader();
    }

    this.bindGeoRefCheck();
  },

  loadingTiles: function() {
    this.loader.show()
  },

  loadTiles: function() {
    this.loader.hide()
  },

  featureOver: function(e, latlon, pxPos, data) {
    if(this.infowindowModel.get('disabled')) return;
    this.mapView.setCursor('pointer');
    this.featureHovered = data;
  },

  featureOut: function() {
    if(this.infowindowModel.get('disabled')) return;
    this.mapView.setCursor('auto');
    this.featureHovered = null;
  },

  featureClick: function(e, latlon, pxPos, data) {
    if(this.infowindowModel.get('disabled')) return;
    if(!this.geometryEditor.isEditing()) {
      if(data.cartodb_id) {
        this.infowindow
        .setLatLng(latlon)
        .setFeatureInfo(data.cartodb_id)
        .showInfowindow();
      } else {
        cdb.log.error("can't show infowindow, no cartodb_id on data");
      }
    }
  },

  /**
   *  Move all necessary blocks when panel is openned (normal, narrowed,...) or closed
   */
  _moveInfo: function(type) {
    if (type == "show") {
      this.$el
        .removeClass('narrow')
        .addClass('displaced');
    } else if (type == "hide") {
      this.$el.removeClass('narrow displaced');
    } else if (type == "narrow") {
      this.$el.addClass('narrow displaced');
    }
  },

  render: function() {
    this.$el.html('');
    this.$el.append(this.template());
    return this;
  },

  showDataLayer: function() {
    this.layerDataView.setInteraction(true);
    this.layerDataView.setOpacity(1.0);
  },

  hideDataLayer: function() {
    //this.layerDataView.hide();
    this.layerDataView.setInteraction(false);
    this.layerDataView.setOpacity(0.5);
  },

  /**
  * reload tiles
  */
  updateDataLayerView: function() {
    if(this.layerDataView) {
      this.layerDataView.reload();
    }
  },
  /**
  * Paints a dialog with a warning when the user hasn't any georeferenced row
  * @method showNoGeorefWarning
  */
  showNoGeoRefWarning: function() {
    var warningStorageName = '';
    if(this.table.data().length > 0) {
      warningStorageName = 'georefContentWarningShowed' + this.table.id;
    } else {
      warningStorageName = 'georefNoContentWarningShowed' + this.table.id;
    }

    if(!this.noGeoRefDialog && !this.table.isInSQLView() && (!localStorage[warningStorageName])) {
      localStorage[warningStorageName] = true;
      this.noGeoRefDialog = new cdb.admin.NoGeoRefDataDialog({
        model: this.table
      });
      this.retrigger('georeference', this.noGeoRefDialog);
      this.retrigger('manual', this.noGeoRefDialog);
      this.noGeoRefDialog.render().$el.appendTo(this.$el);
    }
    // if the dialog already has been shown, we don't show it again
  },

  //adds the green indicator when a query is applied
  _addSQLViewHeader: function() {
    this.$('.sqlview').remove();
    var total = this.table.data().size();
    var html = this.getTemplate('table/views/sql_view_notice')({
      empty: !total
    });
    this.$('.cartodb-map').after(html);
  },

  _removeSQLViewHeader: function() {
    this.$('.sqlview').remove();
  },

  _clearView: function(e) {
    this.killEvent(e);
    this.activeLayerView.model.clearSQLView();
    return false;
  },

  _tableFromQuery: function(e) {
    this.killEvent(e);

    var duplicate_dialog = new cdb.admin.DuplicateTableDialog({
      model: this.table
    });
    duplicate_dialog.appendToBody().open();
  }

});

<|endoftext|>


<commit_before>define([
    'jquery',
    '/bower_components/nthen/index.js',
    '/common/sframe-common.js',
    '/common/sframe-app-framework.js',
    '/common/common-util.js',
    '/common/common-hash.js',
    '/common/common-interface.js',
    '/common/modes.js',
    '/customize/messages.js',
    '/kanban/jkanban.js',
    'css!/kanban/jkanban.css',
], function (
    $,
    nThen,
    SFCommon,
    Framework,
    Util,
    Hash,
    UI,
    Modes,
    Messages)
{

    var verbose = function (x) { console.log(x); };
    verbose = function () {}; // comment out to enable verbose logging

    // Kanban code
    var initKanban = function (framework, boards) {
        var defaultBoards = [{
            "id": "todo",
            "title": Messages.kanban_todo,
            "color": "blue",
            "item": [{
                "title": Messages._getKey('kanban_item', [1])
            }, {
                "title": Messages._getKey('kanban_item', [2])
            }]
        }, {
            "id": "working",
            "title": Messages.kanban_working,
            "color": "orange",
            "item": [{
                "title": Messages._getKey('kanban_item', [3])
            }, {
                "title": Messages._getKey('kanban_item', [4])
            }]
        }, {
            "id": "done",
            "title": Messages.kanban_done,
            "color": "green",
            "item": [{
                "title": Messages._getKey('kanban_item', [5])
            }, {
                "title": Messages._getKey('kanban_item', [6])
            }]
        }];

        if (!boards) {
            verbose("Initializing with default boards content");
            boards = defaultBoards;
        } else {
            verbose("Initializing with boards content " + boards);
        }

        // Remove any existing elements
        $(".kanban-container-outer").remove();

        var getInput = function () {
            return $('<input>', {
                'type': 'text',
                'id': 'kanban-edit',
                'size': '30'
            });
        };

        var kanban = new window.jKanban({
            element: '#cp-app-kanban-content',
            gutter: '15px',
            widthBoard: '300px',
            onChange: function () {
                verbose("Board object has changed");
                framework.localChange();
            },
            click: function (el) {
                if (kanban.inEditMode) {
                    verbose("An edit is already active");
                    return;
                }
                kanban.inEditMode = true;
                var name = $(el).text();
                $(el).html('');
                var $input = getInput().val(name).appendTo(el).focus();
                $input[0].select();
                var save = function () {
                    // Store the value
                    var name = $input.val();
                    // Remove the input
                    $(el).text(name);
                    // Save the value for the correct board
                    var board = $(el.parentNode.parentNode).attr("data-id");
                    var pos = kanban.findElementPosition(el);
                    kanban.getBoardJSON(board).item[pos].title = name;
                    kanban.onChange();
                    // Unlock edit mode
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });

            },
            boardTitleClick: function (el, e) {
                e.stopPropagation();
                if (kanban.inEditMode) {
                    verbose("An edit is already active");
                    return;
                }
                kanban.inEditMode = true;
                var name = $(el).text();
                $(el).html('');
                var $input = getInput().val(name).appendTo(el).focus();
                $input[0].select();
                var save = function () {
                    // Store the value
                    var name = $input.val();
                    // Remove the input
                    $(el).text(name);
                    // Save the value for the correct board
                    var board = $(el.parentNode.parentNode).attr("data-id");
                    kanban.getBoardJSON(board).title = name;
                    kanban.onChange();
                    // Unlock edit mode
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });
            },
            colorClick: function (el) {
                verbose("in color click");
                var board = $(el.parentNode).attr("data-id");
                var boardJSON = kanban.getBoardJSON(board);
                var currentColor = boardJSON.color;
                verbose("Current color " + currentColor);
                var index = kanban.options.colors.findIndex(function (element) {
                    return (element === currentColor);
                }) + 1;
                verbose("Next index " + index);
                if (index >= kanban.options.colors.length) { index = 0; }
                var nextColor = kanban.options.colors[index];
                verbose("Next color " + nextColor);
                boardJSON.color = nextColor;
                $(el).removeClass("kanban-header-" + currentColor);
                $(el).addClass("kanban-header-" + nextColor);
                kanban.onChange();

            },
            removeClick: function (el) {
                UI.confirm(Messages.kanban_deleteBoard, function (yes) {
                    if (!yes) { return; }
                    verbose("Delete board");
                    var boardName = $(el.parentNode.parentNode).attr("data-id");
                    for (var index in kanban.options.boards) {
                        if (kanban.options.boards[index].id === boardName) {
                            break;
                        }
                        index++;
                    }
                    kanban.options.boards.splice(index, 1);
                    kanban.removeBoard(boardName);
                    kanban.onChange();
                });
            },
            buttonClick: function (el, boardId, e) {
                e.stopPropagation();
                if (kanban.inEditMode) {
                    verbose("An edit is already active");
                    return;
                }
                kanban.inEditMode = true;
                // create a form to enter element
                var $item = $('<div>', {'class': 'kanban-item'});
                var $input = getInput().val(name).appendTo($item);
                kanban.addForm(boardId, $item[0]);
                $input.focus();
                var save = function () {
                    $item.remove();
                    kanban.addElement(boardId, {
                        "title": $input.val(),
                    });
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });
            },
            addItemButton: true,
            boards: boards,
            dragcancelEl: function (el, boardId) {
                var pos = kanban.findElementPosition(el);
                UI.confirm(Messages.kanban_deleteItem, function (yes) {
                    if (!yes) { return; }
                    var board;
                    kanban.options.boards.some(function (b) {
                        if (b.id === boardId) {
                            return (board = b);
                        }
                    });
                    if (!board) { return; }
                    board.item.splice(pos, 1);
                    $(el).remove();
                    kanban.onChange();
                });
            }
        });

        var addBoardDefault = document.getElementById('kanban-addboard');
        addBoardDefault.addEventListener('click', function () {
            var counter = 1;

            // Get the new board id
            while (kanban.options.boards.indexOf("board" + counter) !== -1) {
                counter++;
            }

            kanban.addBoards([{
                "id": "board" + counter,
                "title": Messages.kanban_newBoard,
                "color": "yellow",
                "item": [{
                    "title": Messages._getKey('kanban_item', [1]),
                }]
            }]);
            kanban.onChange();
        });

        return kanban;
    };

    // Start of the main loop
    var andThen2 = function (framework) {

        var kanban = initKanban(framework);

        framework.onContentUpdate(function (newContent) {
            // Need to update the content
            verbose("Content should be updated to " + newContent);
            var currentContent = kanban.getBoardsJSON();
            var remoteContent = newContent.content;

            if (currentContent !== remoteContent) {
               // reinit kanban (TODO: optimize to diff only)
               verbose("Content is different.. Applying content");
               kanban.setBoards(remoteContent);
            }
        });

        framework.setContentGetter(function () {
            // var content = $("#cp-app-kanban-content").val();
            var content = kanban.getBoardsJSON();
            verbose("Content current value is " + content);
            return {
                content: content
            };
        });

        framework.onReady(function () {
            $("#cp-app-kanban-content").focus();
        });

        framework.start();
    };

    var main = function () {
        // var framework;
        nThen(function (waitFor) {

            // Framework initialization
            Framework.create({
                toolbarContainer: '#cme_toolbox',
                contentContainer: '#cp-app-kanban-editor',
            }, waitFor(function (framework) {
                andThen2(framework);
            }));
        });
    };
    main();
});
<commit_msg>Fix kanban inputs blocked when remote change<commit_after>define([
    'jquery',
    '/bower_components/nthen/index.js',
    '/common/sframe-common.js',
    '/common/sframe-app-framework.js',
    '/common/common-util.js',
    '/common/common-hash.js',
    '/common/common-interface.js',
    '/common/modes.js',
    '/customize/messages.js',
    '/kanban/jkanban.js',
    'css!/kanban/jkanban.css',
], function (
    $,
    nThen,
    SFCommon,
    Framework,
    Util,
    Hash,
    UI,
    Modes,
    Messages)
{

    var verbose = function (x) { console.log(x); };
    verbose = function () {}; // comment out to enable verbose logging

    // Kanban code
    var initKanban = function (framework, boards) {
        var defaultBoards = [{
            "id": "todo",
            "title": Messages.kanban_todo,
            "color": "blue",
            "item": [{
                "title": Messages._getKey('kanban_item', [1])
            }, {
                "title": Messages._getKey('kanban_item', [2])
            }]
        }, {
            "id": "working",
            "title": Messages.kanban_working,
            "color": "orange",
            "item": [{
                "title": Messages._getKey('kanban_item', [3])
            }, {
                "title": Messages._getKey('kanban_item', [4])
            }]
        }, {
            "id": "done",
            "title": Messages.kanban_done,
            "color": "green",
            "item": [{
                "title": Messages._getKey('kanban_item', [5])
            }, {
                "title": Messages._getKey('kanban_item', [6])
            }]
        }];

        if (!boards) {
            verbose("Initializing with default boards content");
            boards = defaultBoards;
        } else {
            verbose("Initializing with boards content " + boards);
        }

        // Remove any existing elements
        $(".kanban-container-outer").remove();

        var getInput = function () {
            return $('<input>', {
                'type': 'text',
                'id': 'kanban-edit',
                'size': '30'
            });
        };

        var kanban = new window.jKanban({
            element: '#cp-app-kanban-content',
            gutter: '15px',
            widthBoard: '300px',
            onChange: function () {
                verbose("Board object has changed");
                framework.localChange();
            },
            click: function (el) {
                if (kanban.inEditMode) {
                    verbose("An edit is already active");
                    return;
                }
                kanban.inEditMode = true;
                var name = $(el).text();
                $(el).html('');
                var $input = getInput().val(name).appendTo(el).focus();
                $input[0].select();
                var save = function () {
                    // Store the value
                    var name = $input.val();
                    // Remove the input
                    $(el).text(name);
                    // Save the value for the correct board
                    var board = $(el.parentNode.parentNode).attr("data-id");
                    var pos = kanban.findElementPosition(el);
                    kanban.getBoardJSON(board).item[pos].title = name;
                    kanban.onChange();
                    // Unlock edit mode
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });

            },
            boardTitleClick: function (el, e) {
                e.stopPropagation();
                if (kanban.inEditMode) {
                    verbose("An edit is already active");
                    return;
                }
                kanban.inEditMode = true;
                var name = $(el).text();
                $(el).html('');
                var $input = getInput().val(name).appendTo(el).focus();
                $input[0].select();
                var save = function () {
                    // Store the value
                    var name = $input.val();
                    // Remove the input
                    $(el).text(name);
                    // Save the value for the correct board
                    var board = $(el.parentNode.parentNode).attr("data-id");
                    kanban.getBoardJSON(board).title = name;
                    kanban.onChange();
                    // Unlock edit mode
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });
            },
            colorClick: function (el) {
                verbose("in color click");
                var board = $(el.parentNode).attr("data-id");
                var boardJSON = kanban.getBoardJSON(board);
                var currentColor = boardJSON.color;
                verbose("Current color " + currentColor);
                var index = kanban.options.colors.findIndex(function (element) {
                    return (element === currentColor);
                }) + 1;
                verbose("Next index " + index);
                if (index >= kanban.options.colors.length) { index = 0; }
                var nextColor = kanban.options.colors[index];
                verbose("Next color " + nextColor);
                boardJSON.color = nextColor;
                $(el).removeClass("kanban-header-" + currentColor);
                $(el).addClass("kanban-header-" + nextColor);
                kanban.onChange();

            },
            removeClick: function (el) {
                UI.confirm(Messages.kanban_deleteBoard, function (yes) {
                    if (!yes) { return; }
                    verbose("Delete board");
                    var boardName = $(el.parentNode.parentNode).attr("data-id");
                    for (var index in kanban.options.boards) {
                        if (kanban.options.boards[index].id === boardName) {
                            break;
                        }
                        index++;
                    }
                    kanban.options.boards.splice(index, 1);
                    kanban.removeBoard(boardName);
                    kanban.onChange();
                });
            },
            buttonClick: function (el, boardId, e) {
                e.stopPropagation();
                if (kanban.inEditMode) {
                    verbose("An edit is already active");
                    return;
                }
                kanban.inEditMode = true;
                // create a form to enter element
                var $item = $('<div>', {'class': 'kanban-item'});
                var $input = getInput().val(name).appendTo($item);
                kanban.addForm(boardId, $item[0]);
                $input.focus();
                var save = function () {
                    $item.remove();
                    kanban.addElement(boardId, {
                        "title": $input.val(),
                    });
                    kanban.inEditMode = false;
                };
                $input.blur(save);
                $input.keydown(function (e) {
                    if (e.which === 13) {
                        e.preventDefault();
                        e.stopPropagation();
                        save();
                        return;
                    }
                    if (e.which === 27) {
                        e.preventDefault();
                        e.stopPropagation();
                        $(el).text(name);
                        kanban.inEditMode = false;
                        return;
                    }
                });
            },
            addItemButton: true,
            boards: boards,
            dragcancelEl: function (el, boardId) {
                var pos = kanban.findElementPosition(el);
                UI.confirm(Messages.kanban_deleteItem, function (yes) {
                    if (!yes) { return; }
                    var board;
                    kanban.options.boards.some(function (b) {
                        if (b.id === boardId) {
                            return (board = b);
                        }
                    });
                    if (!board) { return; }
                    board.item.splice(pos, 1);
                    $(el).remove();
                    kanban.onChange();
                });
            }
        });

        var addBoardDefault = document.getElementById('kanban-addboard');
        addBoardDefault.addEventListener('click', function () {
            var counter = 1;

            // Get the new board id
            while (kanban.options.boards.indexOf("board" + counter) !== -1) {
                counter++;
            }

            kanban.addBoards([{
                "id": "board" + counter,
                "title": Messages.kanban_newBoard,
                "color": "yellow",
                "item": [{
                    "title": Messages._getKey('kanban_item', [1]),
                }]
            }]);
            kanban.onChange();
        });

        return kanban;
    };

    // Start of the main loop
    var andThen2 = function (framework) {

        var kanban = initKanban(framework);

        framework.onContentUpdate(function (newContent) {
            // Need to update the content
            verbose("Content should be updated to " + newContent);
            var currentContent = kanban.getBoardsJSON();
            var remoteContent = newContent.content;

            if (currentContent !== remoteContent) {
                // reinit kanban (TODO: optimize to diff only)
                verbose("Content is different.. Applying content");
                kanban.setBoards(remoteContent);
                kanban.inEditMode = false;
            }
        });

        framework.setContentGetter(function () {
            // var content = $("#cp-app-kanban-content").val();
            var content = kanban.getBoardsJSON();
            verbose("Content current value is " + content);
            return {
                content: content
            };
        });

        framework.onReady(function () {
            $("#cp-app-kanban-content").focus();
        });

        framework.start();
    };

    var main = function () {
        // var framework;
        nThen(function (waitFor) {

            // Framework initialization
            Framework.create({
                toolbarContainer: '#cme_toolbox',
                contentContainer: '#cp-app-kanban-editor',
            }, waitFor(function (framework) {
                andThen2(framework);
            }));
        });
    };
    main();
});
<|endoftext|>


<commit_before>// GET /chat/{id}

TEST("The response has valid return code and content type.", function(Query) {

    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],{},auth).id();
    return Query.get(["db/",db,"/chat/",id],auth)
	.assertStatus(200).assertIsJson();
});

TEST("Returns correct data as admin.", function(Query) {

    var created = {
	subject: "Hello, world!",
	custom: { a: true },
	audience: { admin: "anyone" }
    };

    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],created,auth).id();
    return Query.get(["db/",db,"/chat/",id],auth).then(function(value) {

	var expected = $.extend({
	    id: id,
	    count: 0,
	    access: [ "view","read","admin","moderate","write" ]
	}, created);

	delete value.info.last;

	return Assert.areEqual(expected, value.info);

    });

});

TODO("Returns correct subject.", function(next) {
    Assert.fail();
});

TODO("Returns 404 when database does not exist.", function(next) {
    Assert.fail();
});

TODO("Returns 404 when chatroom does not exist.", function(next) {
    Assert.fail();
});

TODO("Returns 401 when token is not valid.", function(next) {
    Assert.fail();
});

<commit_msg>Test GET /chat/{id}<commit_after>// GET /chat/{id}

TEST("The response has valid return code and content type.", function(Query) {

    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],{},auth).id();
    return Query.get(["db/",db,"/chat/",id],auth)
	.assertStatus(200).assertIsJson();
});

TEST("Returns correct data as admin.", function(Query) {

    var created = {
	subject: "Hello, world!",
	custom: { a: true },
	audience: { admin: "anyone" }
    };

    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],created,auth).id();
    return Query.get(["db/",db,"/chat/",id],auth).then(function(value) {

	var expected = $.extend({
	    id: id,
	    count: 0,
	    access: [ "view","read","admin","moderate","write" ]
	}, created);

	delete value.info.last;

	return Assert.areEqual(expected, value.info);

    });

});

TEST("Returns correct data as moderator.", function(Query) {

    var created = {
	subject: "Hello, world!",
	custom: { a: true },
	audience: { moderate: "anyone" }
    };

    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],created,auth).id();
    return Query.get(["db/",db,"/chat/",id]).then(function(value) {

	var expected = $.extend({
	    id: id,
	    count: 0,
	    access: [ "view","read","moderate","write" ]
	}, created);

	expected.audience = null; // Not available to non-admins

	delete value.info.last;

	return Assert.areEqual(expected, value.info);

    });

});

TEST("Returns correct data as viewer.", function(Query) {

    var created = {
	subject: "Hello, world!",
	custom: { a: true },
	audience: { view: "anyone" }
    };

    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],created,auth).id();
    return Query.get(["db/",db,"/chat/",id]).then(function(value) {

	var expected = $.extend({
	    id: id,
	    count: null, // Not available to non-readers
	    access: [ "view" ]
	}, created);

	expected.audience = null; // Not available to non-admins

	delete value.info.last;

	return Assert.areEqual(expected, value.info);

    });

});

TEST("Returns 404 when database does not exist.", function(Query) {
    return Query.get(["db/00000000000/chat/00000000001"]).assertStatus(404);
});

TEST("Returns 404 when chatroom does not exist.", function(Query) {
    var db = Query.mkdb();
    var auth = Query.auth(db);
    return Query.get(["db/",db,"/chat/00000000000"],auth).assertStatus(404);
});

TEST("Returns 404 when no 'view' access.", function(Query) {
    var db = Query.mkdb();
    var auth = Query.auth(db);
    var id = Query.post(["db/",db,"/chat"],{ audience: {} },auth).id();
    return Query.get(["db/",db,"/chat/",id]).assertStatus(404);
});

<|endoftext|>


<commit_before>// ------------------------------ CRUD -------------------------------------
var Requests = {
  // Record create along ajax
	createByAjax: function(grid, continue_on) {
		var createFormElement = $('div#'+grid.name+'-form form');
		// clear all the error messages
		createFormElement.find(".field_error").text("");
		$.ajax({
   		type:'POST',
   		url: grid.path + '.json',
   		data: createFormElement.serialize() + "&authenticity_token=" + window._token,
   		success: function(request) {
				if (request.success) {
					gridManager.createdIds.push(request.id);
					grid.loader.reloadData();
					if (!continue_on) {
					  Ui.resetForm(grid.name);
            if (grid.loader.isDataLoaded()) {
					    setTimeout(function(){
    					  Ui.closeDialog(grid.name);
    					}, 100);
					  }
					}
					displayNewNotification('Record successfully created!');
				} else {
					for(key in request.error_message){
					  createFormElement.find(".field[name=" + key + "]").find(".field_error").text(request.error_message[key].join());
					}
				}
			}
 		});
	},

	// Delete rows along ajax
	deleteByAjax: function(grid_or_model, ids) {
    var model = typeof(grid_or_model) == 'object' ? grid_or_model.path : grid_or_model;
		$.ajax({
			type: 'POST',
			url: model + '/' + ids + '.json',
			data: decodeURIComponent($.param({_method: 'DELETE', authenticity_token: window._token})),
			success: function(msg) {
				if(msg.success) {
          if(typeof(grid_or_model) == 'object'){
					 grid_or_model.setSelectedRows([]);
					 grid_or_model.loader.reloadData();
          }
				  var recordSize = $.isArray(ids) ? ids.length : ids.split(',').length;
				  var message;
				  if (recordSize > 1) {  message = recordSize+" records have been deleted."; }
				    else { message = "One record has been deleted."; }
				  displayNewNotification(message);
				} else {
					displayErrorMessage(msg.error_message);
				}
			}
		})
	},

	updateByAjax: function(grid, item) {
		delete item.slick_index;
		var currentRow = grid.getRowByRecordId(item.id).index;
		// format item data like time, date
		// format_data(item);
		// put ajax
		$.ajax({
			type: "POST",
			dateType: 'json',
			url: grid.path + "/" + item.id + ".json"+grid.query,
			data: decodeURIComponent($.param({_method: 'PUT', item: item, authenticity_token: window._token})),
			success: function(msg) {
				if(msg.success) {
					grid.loader.reloadData();
          // grid.loader.data[currentRow] = Ui.formatData(grid, msg["attrs"]);
          // grid.updateRow(currentRow);
				} else {
					displayErrorMessage(msg.error_message);
					grid.loader.reloadData();
				}
			}
		});
	},

	batchUpdateByAjax: function(grid) {
	  var ids, width, height, selectedIndexes = grid.getSelectedRows(), originTitle, newTitle,
    scope = $('#' + grid.name + '-form');
	  if ($.isEmptyObject(selectedIndexes)) {
	    displayErrorMessage('Please select a record');
	  } else {
	    ids = Ui.selectIds(grid);
	    if (grid.extend_options) {
        width = grid.extend_options.form_dialog_width || 600;
        height = grid.extend_options.form_dialog_height || 300;
      } else {
        width = 600;
        height = 300;
      }
      originTitle = scope.attr('title');
      newTitle = originTitle.replace('Create new', 'Update');
      scope.attr('title', newTitle);
	    scope.dialog({
        height: height,
        width: width,
        show: "blind",
        modal: true,
        create: function(event, ui) {
          Ui.setupForm(grid.name, true);

          // Switch to update button
          $('.btn', scope).hide();
          $('.update_btn', scope).show();

          // Show flag checkbox
          $('input.target_flag', scope).show();

          // Check flag when change value of the box
          scope.off('keyup', 'input:text').on('keyup', 'input', function(e) {
            $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]', scope).attr('checked', 'checked');
          });
          scope.off('change', 'input:checkbox').on('change', 'input:checkbox', function(e) {
            $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]', scope).attr('checked', 'checked');
          });

          // Empty input box when flag change to unchecked
          scope.off('change', 'input.target_flag:visible').on('change', 'input.target_flag:visible', function(){
            if ($.isEmptyObject($(this).attr('checked'))) {
              $('input[data-target="' + $(this).attr('data-target') + '"]').not(':button, :submit, :reset, :hidden, .target_flag').val('').removeAttr('checked').removeAttr('selected');
              $('select[data-target="' + $(this).attr('data-target') + '"]').val('').trigger("liszt:updated");
            }
          });

          // Submit the form
          scope.off('click', '.update_btn').on('click', '.update_btn', function() {
    				var originArr = $('form', scope).serializeArray(),
    				checkedArr,
    				objectName = $('form', scope).attr('class').replace(/new_/,'');

    				// Collect attrs along with checked flag
    				checkedArr = $.map($('input.target_flag:visible:checked'), function(v) {
    				  var targetInput = $('[data-target="' + $(v).attr('data-target') + '"]').not(':button, :submit, :reset, .target_flag'),
    				  name = targetInput.attr('name').replace(/.*?\[/,'item[');
    				  return { name: name, value: (targetInput.val() || null)};
    				});

            // Collect valid form attrbutes
            originArr = $.grep(originArr, function(v, i) {
              return $('input.target_flag:checkbox[data-target="' + $('[name="' + v.name + '"]').attr('data-target') + '"]', scope).attr('checked') == 'checked'
            });

            // Replace objectName to item, like user[posts][] => item[posts][]
            $.each(originArr, function(i, v) {
              if (originArr[i].name.indexOf(objectName + "[") != -1) {
                originArr[i].name = v.name.replace(/.*?\[/,'item[');
              }
            });

            // Merge the attrs
            checkedArr = $.extend(checkedArr, originArr);

            // Update ajax request
            $.ajax({
              type: "POST",
              dateType: 'json',
              url: grid.path + "/" + ids + ".json"+grid.query,
              data: decodeURIComponent($.param({_method: 'PUT', authenticity_token: window._token}) + '&' + $.param(checkedArr)),
              success: function(msg) {
                if(msg.success) {
                  grid.setSelectedRows([]);
                  grid.loader.reloadData();
                  displayNewNotification(selectedIndexes.length + ' records been updated!');
                } else {
                  displayErrorMessage(msg.error_message);
                  grid.loader.reloadData();
                }
                scope.dialog("destroy");
              }
            });
    			  return false;
    			});
  			},
  			open: function(event, ui) {
  			  // Switch to update button
          $('.btn', scope).hide();
          $('.update_btn', scope).show();

          // Show flag checkbox
          $('input.target_flag', scope).show();
  			},
        close: function(event, ui) {
          $(this).find("input:text").val("");
          $(this).find(".field_error").text("");
          $(this).dialog("destroy");

          scope.attr('title', originTitle);
          $('.btn', scope).show();
          $('.update_btn', scope).hide();
          $('.target_flag').hide();
        }
      });
	  }
	},
}; // Requests<commit_msg>MISC - revert the delete by ajax method<commit_after>// ------------------------------ CRUD -------------------------------------
var Requests = {
  // Record create along ajax
	createByAjax: function(grid, continue_on) {
		var createFormElement = $('div#'+grid.name+'-form form');
		// clear all the error messages
		createFormElement.find(".field_error").text("");
		$.ajax({
   		type:'POST',
   		url: grid.path + '.json',
   		data: createFormElement.serialize() + "&authenticity_token=" + window._token,
   		success: function(request) {
				if (request.success) {
					gridManager.createdIds.push(request.id);
					grid.loader.reloadData();
					if (!continue_on) {
					  Ui.resetForm(grid.name);
            if (grid.loader.isDataLoaded()) {
					    setTimeout(function(){
    					  Ui.closeDialog(grid.name);
    					}, 100);
					  }
					}
					displayNewNotification('Record successfully created!');
				} else {
					for(key in request.error_message){
					  createFormElement.find(".field[name=" + key + "]").find(".field_error").text(request.error_message[key].join());
					}
				}
			}
 		});
	},

	// Delete rows along ajax
	deleteByAjax: function(grid, ids) {
		$.ajax({
			type: 'POST',
			url: grid.path + '/' + ids + '.json',
			data: decodeURIComponent($.param({_method: 'DELETE', authenticity_token: window._token})),
			success: function(msg) {
				if(msg.success) {
					grid.setSelectedRows([]);
					grid.loader.reloadData();
				  var recordSize = $.isArray(ids) ? ids.length : ids.split(',').length;
				  var message;
				  if (recordSize > 1) {  message = recordSize+" records have been deleted."; }
				    else { message = "One record has been deleted."; }
				  displayNewNotification(message);
				} else {
					displayErrorMessage(msg.error_message);
				}
			}
		});
	},

	updateByAjax: function(grid, item) {
		delete item.slick_index;
		var currentRow = grid.getRowByRecordId(item.id).index;
		// format item data like time, date
		// format_data(item);
		// put ajax
		$.ajax({
			type: "POST",
			dateType: 'json',
			url: grid.path + "/" + item.id + ".json"+grid.query,
			data: decodeURIComponent($.param({_method: 'PUT', item: item, authenticity_token: window._token})),
			success: function(msg) {
				if(msg.success) {
					grid.loader.reloadData();
          // grid.loader.data[currentRow] = Ui.formatData(grid, msg["attrs"]);
          // grid.updateRow(currentRow);
				} else {
					displayErrorMessage(msg.error_message);
					grid.loader.reloadData();
				}
			}
		});
	},

	batchUpdateByAjax: function(grid) {
	  var ids, width, height, selectedIndexes = grid.getSelectedRows(), originTitle, newTitle,
    scope = $('#' + grid.name + '-form');
	  if ($.isEmptyObject(selectedIndexes)) {
	    displayErrorMessage('Please select a record');
	  } else {
	    ids = Ui.selectIds(grid);
	    if (grid.extend_options) {
        width = grid.extend_options.form_dialog_width || 600;
        height = grid.extend_options.form_dialog_height || 300;
      } else {
        width = 600;
        height = 300;
      }
      originTitle = scope.attr('title');
      newTitle = originTitle.replace('Create new', 'Update');
      scope.attr('title', newTitle);
	    scope.dialog({
        height: height,
        width: width,
        show: "blind",
        modal: true,
        create: function(event, ui) {
          Ui.setupForm(grid.name, true);

          // Switch to update button
          $('.btn', scope).hide();
          $('.update_btn', scope).show();

          // Show flag checkbox
          $('input.target_flag', scope).show();

          // Check flag when change value of the box
          scope.off('keyup', 'input:text').on('keyup', 'input', function(e) {
            $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]', scope).attr('checked', 'checked');
          });
          scope.off('change', 'input:checkbox').on('change', 'input:checkbox', function(e) {
            $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]', scope).attr('checked', 'checked');
          });

          // Empty input box when flag change to unchecked
          scope.off('change', 'input.target_flag:visible').on('change', 'input.target_flag:visible', function(){
            if ($.isEmptyObject($(this).attr('checked'))) {
              $('input[data-target="' + $(this).attr('data-target') + '"]').not(':button, :submit, :reset, :hidden, .target_flag').val('').removeAttr('checked').removeAttr('selected');
              $('select[data-target="' + $(this).attr('data-target') + '"]').val('').trigger("liszt:updated");
            }
          });

          // Submit the form
          scope.off('click', '.update_btn').on('click', '.update_btn', function() {
    				var originArr = $('form', scope).serializeArray(),
    				checkedArr,
    				objectName = $('form', scope).attr('class').replace(/new_/,'');

    				// Collect attrs along with checked flag
    				checkedArr = $.map($('input.target_flag:visible:checked'), function(v) {
    				  var targetInput = $('[data-target="' + $(v).attr('data-target') + '"]').not(':button, :submit, :reset, .target_flag'),
    				  name = targetInput.attr('name').replace(/.*?\[/,'item[');
    				  return { name: name, value: (targetInput.val() || null)};
    				});

            // Collect valid form attrbutes
            originArr = $.grep(originArr, function(v, i) {
              return $('input.target_flag:checkbox[data-target="' + $('[name="' + v.name + '"]').attr('data-target') + '"]', scope).attr('checked') == 'checked'
            });

            // Replace objectName to item, like user[posts][] => item[posts][]
            $.each(originArr, function(i, v) {
              if (originArr[i].name.indexOf(objectName + "[") != -1) {
                originArr[i].name = v.name.replace(/.*?\[/,'item[');
              }
            });

            // Merge the attrs
            checkedArr = $.extend(checkedArr, originArr);

            // Update ajax request
            $.ajax({
              type: "POST",
              dateType: 'json',
              url: grid.path + "/" + ids + ".json"+grid.query,
              data: decodeURIComponent($.param({_method: 'PUT', authenticity_token: window._token}) + '&' + $.param(checkedArr)),
              success: function(msg) {
                if(msg.success) {
                  grid.setSelectedRows([]);
                  grid.loader.reloadData();
                  displayNewNotification(selectedIndexes.length + ' records been updated!');
                } else {
                  displayErrorMessage(msg.error_message);
                  grid.loader.reloadData();
                }
                scope.dialog("destroy");
              }
            });
    			  return false;
    			});
  			},
  			open: function(event, ui) {
  			  // Switch to update button
          $('.btn', scope).hide();
          $('.update_btn', scope).show();

          // Show flag checkbox
          $('input.target_flag', scope).show();
  			},
        close: function(event, ui) {
          $(this).find("input:text").val("");
          $(this).find(".field_error").text("");
          $(this).dialog("destroy");

          scope.attr('title', originTitle);
          $('.btn', scope).show();
          $('.update_btn', scope).hide();
          $('.target_flag').hide();
        }
      });
	  }
	},
}; // Requests<|endoftext|>


<commit_before>import moment from 'moment'
import bunyan from 'bunyan'
const log = bunyan.createLogger({name: 'twitter-realtime-ww2'})

const getCreatedDateTimeFromTweet = (tweet) => moment(tweet.created_at, 'dd MMM DD HH:mm:ss ZZ YYYY', 'en')

const getEventDateTime = (tweet) => {
  const dateTime = getCreatedDateTimeFromTweet(tweet)
  dateTime.set('year', dateTime.get('year') - 78)
  return dateTime.utc().format()
}

const convertTweetToEvent = (tweet) => {
  return {
    datetime: getEventDateTime(tweet),
    content: tweet.full_text.replace(new RegExp('\\s*https://t.co.*$'), '')
  }
}

export default class TwitterRealtimeWw2 {
  constructor (twitterService) {
    this.twitterService = twitterService
  }

  async getLatestEvent () {
    log.info('Getting the latest event ...')
    const latestTweets = await this.twitterService.getLatestTweets()
    const firstTweet = latestTweets[0]
    return convertTweetToEvent(firstTweet)
  }

  async getRecentEvents (durationHour, clock) {
    if (durationHour < 1) {
      throw new MinDurationError()
    }
    if (durationHour > 24) {
      throw new MaxDurationError()
    }

    log.info({durationHour, clock}, 'Getting recent events ...')
    const latestTweets = await this.twitterService.getLatestTweets()
    const recentEvents = latestTweets
      .filter(tweet => {
        const now = moment(clock)
        const minimumDateTime = now.clone().subtract(durationHour, 'hours')
        const tweetDateTime = getCreatedDateTimeFromTweet(tweet)
        return tweetDateTime.diff(minimumDateTime) >= 0 && tweetDateTime.diff(now) <= 0
      })
      .map(convertTweetToEvent)
      .sort((t1, t2) => moment(t1.datetime).diff(moment(t2.datetime)))
    log.info({recentEvents}, 'Finished getting the recent events')
    return recentEvents
  }
}

export class MinDurationError extends Error {
  constructor (message) {
    super(message)
    this.name = MinDurationError.name
  }
}

export class MaxDurationError extends Error {
  constructor (message) {
    super(message)
    this.name = MaxDurationError.name
  }
}
<commit_msg>Refactor and clean up twitter-realtime-ww2.<commit_after>import moment from 'moment'
import bunyan from 'bunyan'

const log = bunyan.createLogger({name: 'twitter-realtime-ww2'})

class MinDurationError extends Error {
  constructor (message) {
    super(message)
    this.name = MinDurationError.name
  }
}

class MaxDurationError extends Error {
  constructor (message) {
    super(message)
    this.name = MaxDurationError.name
  }
}

const validateDurationHour = durationHour => {
  if (durationHour < 1) {
    throw new MinDurationError()
  }
  if (durationHour > 24) {
    throw new MaxDurationError()
  }
}

const getCreatedDateTimeFromTweet = (tweet) => moment(tweet.created_at, 'dd MMM DD HH:mm:ss ZZ YYYY', 'en')

const getEventDateTime = (tweet) => {
  const dateTime = getCreatedDateTimeFromTweet(tweet)
  dateTime.set('year', dateTime.get('year') - 78)
  return dateTime.utc().format()
}

const convertTweetToEvent = tweet => ({
  datetime: getEventDateTime(tweet),
  content: tweet.full_text.replace(new RegExp('\\s*https://t.co.*$'), '')
})

const dateTimeDiff = (d1, d2) => moment(d1).diff(moment(d2))

const eventDateTimeDiff = (e1, e2) => dateTimeDiff(e1.datetime, e2.datetime)

const isBetweenDateRange = (date, min, max) => date.diff(min) >= 0 && date.diff(max) <= 0

const isTweetWithinDuration = (clock, durationHour) => tweet => {
  const now = moment(clock)
  const minimumDateTime = now.clone().subtract(durationHour, 'hours')
  const tweetDateTime = getCreatedDateTimeFromTweet(tweet)
  return isBetweenDateRange(tweetDateTime, minimumDateTime, now)
}

export default class TwitterRealtimeWw2 {
  constructor (twitterService) {
    this.twitterService = twitterService
  }

  async getLatestEvent () {
    log.info('Getting the latest event ...')
    const latestTweets = await this.twitterService.getLatestTweets()
    const firstTweet = latestTweets[0]
    return convertTweetToEvent(firstTweet)
  }

  async getRecentEvents (durationHour, clock) {
    validateDurationHour(durationHour)
    log.info({durationHour, clock}, 'Getting recent events ...')
    const latestTweets = await this.twitterService.getLatestTweets()
    const recentEvents = latestTweets
      .filter(isTweetWithinDuration(clock, durationHour))
      .map(convertTweetToEvent)
      .sort(eventDateTimeDiff)
    log.info({recentEvents}, 'Finished getting the recent events')
    return recentEvents
  }
}

export {MinDurationError, MaxDurationError}
<|endoftext|>


<commit_before>/* global prettyPrint */
(function($) {
	"use strict";

	var $window = $(window),
		$body = $('body'),
		urlHash = window.location.hash,
		scrollBuffer = false,

		// Nav vars
		$kssMenuToggle = $('.kss___header__toggleNav'),
		$kssMenuWrapper = $(".kss___navWrapper"),
		$kssMenu = $(".kss___nav"),
		$kssMenuItems = $kssMenu.find(".kss___nav__item"),
		$kssMenuActiveItem = $kssMenuItems.eq($kssMenu.data("kss-activemenuindex")),
		$kssMenuSub = $(".kss___navSub"),
		$kssMenuSubItems = $kssMenuSub.find(".kss___navSub__item"),
		kssMenuToggleActiveClass = 'kss___header__toggleNav--active',
		kssMenuIsOpenClass = 'kss___navIsOpen',
		kssMenuWrapperWidth = $kssMenuWrapper.outerWidth(),
		kssMenuStickyClass = "kss___nav--sticky",
		kssMenuSubActiveClass = "kss___navSub__item--active",
		kssMenuOffset = $kssMenu.offset().top,

		// Content related vars
		$kssSiteWrapper = $('.kss___siteWrapper'),
		$kssContentWrapper = $('.kss___contentWrapper'),

		// Initial setup of the application.
		init = function() {
			$kssMenuToggle.on("click", function(event){
				event.preventDefault();
				event.stopImmediatePropagation();

				toggleMenu();
			});
			$kssSiteWrapper.on('click', function() {
				closeMenu();
			});

			// Add the active class for the current item in the sideMenu.
			$kssMenuActiveItem.addClass("kss___nav__item--active");

			// Format the template.
			formatTemplate();

			if ($kssMenuSub.length) {
				// Render the submenu.
				renderSubMenu();

				// Set the active class on the current item.
				scrollSpy();
				$window.on("scroll", function() {
					if (!scrollBuffer) {
						scrollBuffer = setTimeout(function () {
							scrollSpy();
							scrollBuffer = false;
						}, 60);
					}
				});
			}

			// Set the height of the sideNav as a min-height on the contentWrapper.
			$kssContentWrapper.css("min-height", $kssMenu.height());

			// Ensure code blocks are highlighted properly.
			prettyPrint();
		},

		renderSubMenu = function() {
			// Add the menu depth classes for each item.
			$kssMenuSubItems.each(function(index, elem) {
				var $this = $(elem),
					$referenceNum = $this.find('.kss___navSub__item__ref'),
					depth = $referenceNum.html().split('.').length;
				$this.addClass('kss___navSub__item--' + depth);
			});

			// Append the subMenu of the current item into the sideMenu.
			$kssMenuSub.appendTo(".kss___nav__item--active");
		},

		scrollSpy = function () {
			var scrollTop = $window.scrollTop(),
				$kssMenuSubAnchors = $kssMenuSub.find("a"),
				currentTargetHash,
				activeIndex;

			// Get the new index for the new active subMenuItem.
			$kssMenuSubAnchors.each(function (index) {
				var anchorHash = $(this).attr("href"),
					$anchorTarget = $(anchorHash.replace(/\./g, "\\.")),
					offsetTop = $anchorTarget.offset().top,
					offsetBottom = offsetTop + $anchorTarget.outerHeight(true);

				if (offsetTop <= scrollTop && scrollTop < offsetBottom) {
					activeIndex = index;
					currentTargetHash = anchorHash;
				}
			});

			// Set the active class on the subMenuItem.
			$kssMenuSubItems.removeClass(kssMenuSubActiveClass);
			if (typeof activeIndex !== "undefined") {
				$kssMenuSubItems.eq(activeIndex).addClass(kssMenuSubActiveClass);
				if(urlHash !== currentTargetHash) {
					history.replaceState(null, null, currentTargetHash);
					urlHash = currentTargetHash;
				}
			} else if(scrollTop < $($kssMenuSubAnchors.eq(0).attr("href").replace(/\./g, "\\.")).offset().top) {
				$kssMenuSubItems.eq(0).addClass(kssMenuSubActiveClass);
			}
		},
		formatTemplate = function() {
			// Modify the page header.
			var pageName = $kssContentWrapper.children('h1').find('.kss-header').html();
			if(pageName) {
				$('.kss_header__pageName').html(' - ' + pageName);
			}

			// Additional section formating
			$('.kss-box > p').each(function(index, elem) {
				var $this = $(elem),
					text = $this.html(),
					regExp = /(^<!= |, !&gt)/;

				if(regExp.test(text)) {
					text = text.replace('<!= ', '').replace(' !>', '');
					var $wrapper = $this.parent().parent(),
						array = text.split(' || ');

					// Check for code only sections.
					if(array.indexOf('isPureCode') !== -1) {
						$wrapper.addClass('kss___hideExamples');
					}

					// Set the optional 'css' code type on the prettyPrint element.
					if(array.indexOf('type: css') !== -1) {
						$wrapper.find('.kss-markup > pre').removeClass('lang-html').addClass('lang-css');
					}

					// Remove the marker element.
					$this.remove();
				}
			});
		},
		toggleMenu = function() {
			if($body.hasClass(kssMenuIsOpenClass)) {
				closeMenu();
			} else {
				openMenu();
			}
		},
		openMenu = function() {
			$kssMenuToggle.addClass(kssMenuToggleActiveClass);
			$body.addClass(kssMenuIsOpenClass);

			// Fallback for older browsers.
			if(!Modernizr.csstransforms) {
				$kssMenuWrapper.show().animate({
					left: '0'
				}, 400 );
				$kssSiteWrapper.animate({
					marginLeft: kssMenuWrapperWidth
				}, 400 );
			}
		},
		closeMenu = function() {
			$kssMenuToggle.removeClass(kssMenuToggleActiveClass);
			$body.removeClass(kssMenuIsOpenClass);

			// Fallback for older browsers.
			if(!Modernizr.csstransforms) {
				$kssMenuWrapper.animate({
					left: '-' + kssMenuWrapperWidth
				}, 400, function(){
					$kssMenuWrapper.hide();
				} );
				$kssSiteWrapper.animate({
					marginLeft: '0'
				}, 400 );
			}
		};

	init();
})(jQuery);
<commit_msg>[TASK] Fix the visibility of styleguide sections when the user navigates via url hashes - resolves #32<commit_after>/* global prettyPrint */
(function($) {
	"use strict";

	var $window = $(window),
		$body = $('body'),
		urlHash = window.location.hash,
		scrollBuffer = false,

		// Nav vars
		$kssMenuToggle = $('.kss___header__toggleNav'),
		$kssMenuWrapper = $(".kss___navWrapper"),
		$kssMenu = $(".kss___nav"),
		$kssMenuItems = $kssMenu.find(".kss___nav__item"),
		$kssMenuActiveItem = $kssMenuItems.eq($kssMenu.data("kss-activemenuindex")),
		$kssMenuSub = $(".kss___navSub"),
		$kssMenuSubItems = $kssMenuSub.find(".kss___navSub__item"),
		kssMenuToggleActiveClass = 'kss___header__toggleNav--active',
		kssMenuIsOpenClass = 'kss___navIsOpen',
		kssMenuWrapperWidth = $kssMenuWrapper.outerWidth(),
		kssMenuStickyClass = "kss___nav--sticky",
		kssMenuSubActiveClass = "kss___navSub__item--active",
		kssMenuOffset = $kssMenu.offset().top,

		// Content related vars
		$kssSiteHeader = $('.kss___header'),
		$kssSiteWrapper = $('.kss___siteWrapper'),
		$kssContentWrapper = $('.kss___contentWrapper'),
		kssSiteHeaderHeight,

		// Initial setup of the application.
		init = function() {
			// Set up some vars
			kssSiteHeaderHeight = $kssSiteHeader[0].offsetHeight;

			// Off-Canvas events
			$kssMenuToggle.on("click", function(event){
				event.preventDefault();
				event.stopImmediatePropagation();

				toggleMenu();
			});
			$kssSiteWrapper.on('click', function() {
				closeMenu();
			});

			// Initial scroll to the given window.location hash.
			if(urlHash) {
				scrollToElement(urlHash.replace('#', ''));
			}

			// Add the active class for the current item in the sideMenu.
			$kssMenuActiveItem.addClass("kss___nav__item--active");

			// Format the template.
			formatTemplate();

			if ($kssMenuSub.length) {
				// Render the submenu.
				renderSubMenu();

				// Set the active class on the current item.
				scrollSpy();
				$window.on("scroll", function() {
					if (!scrollBuffer) {
						scrollBuffer = setTimeout(function () {
							scrollSpy();
							scrollBuffer = false;
						}, 60);
					}
				});
			}

			// ScrollTo section events
			$('.kss___navSub a, .kss-title__refLink').on('click', function(event) {
				event.preventDefault();
				event.stopImmediatePropagation();

				scrollToElement($(this).attr('href').replace('#', ''));
			});

			// Set the height of the sideNav as a min-height on the contentWrapper.
			$kssContentWrapper.css("min-height", $kssMenu.height());

			// Ensure code blocks are highlighted properly.
			prettyPrint();
		},
		renderSubMenu = function() {
			// Add the menu depth classes for each item.
			$kssMenuSubItems.each(function(index, elem) {
				var $this = $(elem),
					$referenceNum = $this.find('.kss___navSub__item__ref'),
					depth = $referenceNum.html().split('.').length;
				$this.addClass('kss___navSub__item--' + depth);
			});

			// Append the subMenu of the current item into the sideMenu.
			$kssMenuSub.appendTo(".kss___nav__item--active");
		},
		scrollSpy = function () {
			var scrollTop = $window.scrollTop(),
				$kssMenuSubAnchors = $kssMenuSub.find("a"),
				currentTargetHash,
				activeIndex;

			// Get the index for the new active subMenuItem.
			$kssMenuSubAnchors.each(function (index) {
				var anchorHash = $(this).attr("href"),
					$anchorTarget = $(anchorHash.replace(/\./g, "\\.")),
					offsetTop = $anchorTarget.offset().top  - kssSiteHeaderHeight,
					offsetBottom = offsetTop + $anchorTarget.outerHeight(true);

				if (offsetTop <= scrollTop && scrollTop < offsetBottom) {
					activeIndex = index;
					currentTargetHash = anchorHash;
				}
			});

			// Set the active class on the subMenuItem.
			$kssMenuSubItems.removeClass(kssMenuSubActiveClass);
			if (typeof activeIndex !== "undefined") {
				$kssMenuSubItems.eq(activeIndex).addClass(kssMenuSubActiveClass);
				if(urlHash !== currentTargetHash) {
					history.replaceState(null, null, currentTargetHash);
					urlHash = currentTargetHash;
				}
			} else if(scrollTop < $($kssMenuSubAnchors.eq(0).attr("href").replace(/\./g, "\\.")).offset().top) {
				$kssMenuSubItems.eq(0).addClass(kssMenuSubActiveClass);
			}
		},
		scrollToElement = function(id) {
			var offset = document.getElementById(id).offsetTop - kssSiteHeaderHeight,
				documentElements = $("html, body");

			documentElements.animate({scrollTop:offset}, '500', 'swing');
		},
		formatTemplate = function() {
			// Modify the page header.
			var pageName = $kssContentWrapper.children('h1').find('.kss-header').html();
			if(pageName) {
				$('.kss_header__pageName').html(' - ' + pageName);
			}

			// Additional section formating
			$('.kss-box > p').each(function(index, elem) {
				var $this = $(elem),
					text = $this.html(),
					regExp = /(^<!= |, !&gt)/;

				if(regExp.test(text)) {
					text = text.replace('<!= ', '').replace(' !>', '');
					var $wrapper = $this.parent().parent(),
						array = text.split(' || ');

					// Check for code only sections.
					if(array.indexOf('isPureCode') !== -1) {
						$wrapper.addClass('kss___hideExamples');
					}

					// Set the optional 'css' code type on the prettyPrint element.
					if(array.indexOf('type: css') !== -1) {
						$wrapper.find('.kss-markup > pre').removeClass('lang-html').addClass('lang-css');
					}

					// Remove the marker element.
					$this.remove();
				}
			});
		},
		toggleMenu = function() {
			if($body.hasClass(kssMenuIsOpenClass)) {
				closeMenu();
			} else {
				openMenu();
			}
		},
		openMenu = function() {
			$kssMenuToggle.addClass(kssMenuToggleActiveClass);
			$body.addClass(kssMenuIsOpenClass);

			// Fallback for older browsers.
			if(!Modernizr.csstransforms) {
				$kssMenuWrapper.show().animate({
					left: '0'
				}, 400 );
				$kssSiteWrapper.animate({
					marginLeft: kssMenuWrapperWidth
				}, 400 );
			}
		},
		closeMenu = function() {
			$kssMenuToggle.removeClass(kssMenuToggleActiveClass);
			$body.removeClass(kssMenuIsOpenClass);

			// Fallback for older browsers.
			if(!Modernizr.csstransforms) {
				$kssMenuWrapper.animate({
					left: '-' + kssMenuWrapperWidth
				}, 400, function(){
					$kssMenuWrapper.hide();
				} );
				$kssSiteWrapper.animate({
					marginLeft: '0'
				}, 400 );
			}
		};

	init();
})(jQuery);
<|endoftext|>


<commit_before>// ---------
// Menu Code
// ---------
module.exports = function(app) {
  var Menu = require('menu')
  var BrowserWindow = require('browser-window')
  var template = [
    {
      label: 'Melange',
      submenu: [
        {
          label: 'About Melange',
          selector: 'orderFrontStandardAboutPanel:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Preferences',
          accelerator: 'Command+,',
          selector: ''
        },
        {
          type: 'separator'
        },
        {
          label: 'Hide Melange',
          accelerator: 'Command+H',
          selector: 'hide:'
        },
        {
          label: 'Hide Others',
          accelerator: 'Command+Shift+H',
          selector: 'hideOtherApplications:'
        },
        {
          label: 'Show All',
          selector: 'unhideAllApplications:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function() { app.quit(); }
        },
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          label: 'Undo',
          accelerator: 'Command+Z',
          selector: 'undo:'
        },
        {
          label: 'Redo',
          accelerator: 'Shift+Command+Z',
          selector: 'redo:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Cut',
          accelerator: 'Command+X',
          selector: 'cut:'
        },
        {
          label: 'Copy',
          accelerator: 'Command+C',
          selector: 'copy:'
        },
        {
          label: 'Paste',
          accelerator: 'Command+V',
          selector: 'paste:'
        },
        {
          label: 'Select All',
          accelerator: 'Command+A',
          selector: 'selectAll:'
        },
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Reload',
          accelerator: 'Command+R',
          click: function() { BrowserWindow.getFocusedWindow().reloadIgnoringCache(); }
        },
        {
          label: 'Toggle DevTools',
          accelerator: 'Alt+Command+I',
          click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
        },
      ]
    },
    {
      label: 'Window',
      submenu: [
        {
          label: 'Minimize',
          accelerator: 'Command+M',
          selector: 'performMiniaturize:'
        },
        {
          label: 'Close',
          accelerator: 'Command+W',
          selector: 'performClose:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Bring All to Front',
          selector: 'arrangeInFront:'
        },
      ]
    },
  ];

  menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}
<commit_msg>Fixed Menu Bar for Windows Users<commit_after>// ---------
// Menu Code
// ---------
module.exports = function(app) {
  var Menu = require('menu')
  var BrowserWindow = require('browser-window')
  var template = [
    {
      label: 'Melange',
      submenu: [
        {
          label: 'About Melange',
          selector: 'orderFrontStandardAboutPanel:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Preferences',
          accelerator: 'CommandOrControl+,',
          click: function() {
            var current = BrowserWindow.getFocusedWindow().webContents;
            current.executeJavaScript("location.hash = '#/settings'");
            // current.executeJavaScript("alert('hi')");
          }
        },
        {
          type: 'separator'
        },
        {
          label: 'Services',
        },
        {
          type: 'separator'
        },
        {
          label: 'Hide Melange',
          accelerator: 'Command+H',
          selector: 'hide:'
        },
        {
          label: 'Hide Others',
          accelerator: 'Command+Shift+H',
          selector: 'hideOtherApplications:'
        },
        {
          label: 'Show All',
          selector: 'unhideAllApplications:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Quit',
          accelerator: 'Command+Q',
          click: function() { app.quit(); }
        },
      ]
    },
    {
      label: 'Edit',
      submenu: [
        {
          label: 'Undo',
          accelerator: 'Command+Z',
          selector: 'undo:'
        },
        {
          label: 'Redo',
          accelerator: 'Shift+Command+Z',
          selector: 'redo:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Cut',
          accelerator: 'Command+X',
          selector: 'cut:'
        },
        {
          label: 'Copy',
          accelerator: 'Command+C',
          selector: 'copy:'
        },
        {
          label: 'Paste',
          accelerator: 'Command+V',
          selector: 'paste:'
        },
        {
          label: 'Select All',
          accelerator: 'Command+A',
          selector: 'selectAll:'
        },
      ]
    },
    {
      label: 'View',
      submenu: [
        {
          label: 'Reload',
          accelerator: 'CommandOrControl+R',
          click: function() { BrowserWindow.getFocusedWindow().reloadIgnoringCache(); }
        },
        {
          label: 'Toggle DevTools',
          accelerator: 'Alt+CommandOrControl+I',
          click: function() { BrowserWindow.getFocusedWindow().toggleDevTools(); }
        },
      ]
    },
    {
      label: 'Window',
      submenu: [
        {
          label: 'Minimize',
          accelerator: 'Command+M',
          selector: 'performMiniaturize:'
        },
        {
          label: 'Close',
          accelerator: 'Command+W',
          selector: 'performClose:'
        },
        {
          type: 'separator'
        },
        {
          label: 'Bring All to Front',
          selector: 'arrangeInFront:'
        },
      ]
    },
    {
      label: 'Help',
      submenu: [],
    },
  ];

  menu = Menu.buildFromTemplate(template);
  Menu.setApplicationMenu(menu);
}
<|endoftext|>