<commit_before>"use strict";

var CityTour = CityTour || {};

CityTour.TerrainGenerator = (function() {
  var MAX_INITIAL_TERRAIN_HEIGHT = 6;
  var HEIGHT_JITTER_PER_ITERATION = 20;
  var HEIGHT_JITTER_DECAY_PER_ITERATION = 0.65;
  var LAND = 'land';
  var WATER = 'water';

  var emptyTerrain = function(columns, rows) {
    var x, z;
    var terrainCoordinates = [];

    for (x = 0; x <= columns; x++) {
      terrainCoordinates[x] = [];

      for (z = 0; z <= rows; z++) {
        terrainCoordinates[x][z] = { material: LAND, height: 0.0 };
      }
    }

    return terrainCoordinates;
  };

  var normalizeCoordinates = function(terrainCoordinates, columns, columnsToGenerate, rows, rowsToGenerate) {
    var x, z;
    var halfColumns = columns / 2;
    var halfRows = rows / 2;
    var columnOffset = halfColumns + ((columnsToGenerate - columns) / 2);
    var rowOffset = halfRows + ((rowsToGenerate - rows) / 2);
    var material;

    var normalizedTerrainCoordinates = [];

    for (x = -halfColumns; x <= halfColumns; x++) {
      normalizedTerrainCoordinates[x] = [];

      for (z = -halfRows; z <= halfRows; z++) {
        normalizedTerrainCoordinates[x][z] = {
          material: terrainCoordinates[x + columnOffset][z + rowOffset].material,
          height: terrainCoordinates[x + columnOffset][z + rowOffset].height,
        }
      }
    }

    return normalizedTerrainCoordinates;
  };

  var nextPowerOfTwo = function(n) {
    return Math.pow(2, Math.ceil(Math.log2(n)));
  };

  var buildTerrainCoordinates = function(columns, rows) {
    var columnsToGenerate = nextPowerOfTwo(columns);
    var rowsToGenerate = nextPowerOfTwo(rows);

    var terrainCoordinates = emptyTerrain(columnsToGenerate, rowsToGenerate);

    // Initial randomization of corners
    terrainCoordinates[0][0].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);
    terrainCoordinates[0][rows].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);
    terrainCoordinates[columns][0].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);
    terrainCoordinates[columns][rows].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);

    // City must be (2^n + 1) blocks on both x and z dimensions for this to work
    midpointDisplace(terrainCoordinates,
                     HEIGHT_JITTER_PER_ITERATION,
                     HEIGHT_JITTER_DECAY_PER_ITERATION,
                     0,
                     rowsToGenerate,
                     columnsToGenerate,
                     0);

    var x, z;

    var minimumRiverBankHeight = Number.POSITIVE_INFINITY;
    var z = (rows / 2) + 4;
    for (x = 0; x <= columns; x++) {
      if (terrainCoordinates[x][z].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[x][z].height;
      }
    }
    var z = rows / 2;
    for (x = 0; x <= columns; x++) {
      if (terrainCoordinates[x][z].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[x][z].height;
      }
    }

    for (z = (rows / 2) + 4; z > (rows / 2); z--) {
      for (x = 0; x <= columns; x++) {
        terrainCoordinates[x][z].height = minimumRiverBankHeight;
      }
    }

    floodFill(terrainCoordinates, 0, (rows / 2) + 4, minimumRiverBankHeight, WATER);

    // Convert to final coordinates
    var finalTerrainCoordinates = normalizeCoordinates(terrainCoordinates, columns, columnsToGenerate, rows, rowsToGenerate);

    return finalTerrainCoordinates;
  };

  var floodFill = function(terrainCoordinates, x, z, height, material) {
    terrainCoordinates[x][z].height = height;
    terrainCoordinates[x][z].material = material;

    if (terrainCoordinates[x - 1] &&
        terrainCoordinates[x - 1][z] &&
        terrainCoordinates[x - 1][z].height <= height &&
        terrainCoordinates[x - 1][z].material != material) {
      floodFill(terrainCoordinates, x - 1, z, height, material);
    }
    if (terrainCoordinates[x + 1] &&
        terrainCoordinates[x + 1][z] &&
        terrainCoordinates[x + 1][z].height <= height &&
        terrainCoordinates[x + 1][z].material != material) {
      floodFill(terrainCoordinates, x + 1, z, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z - 1] &&
        terrainCoordinates[x][z - 1].height <= height &&
        terrainCoordinates[x][z - 1].material != material) {
      floodFill(terrainCoordinates, x, z - 1, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z + 1] &&
        terrainCoordinates[x][z + 1].height <= height &&
        terrainCoordinates[x][z + 1].material != material) {
      floodFill(terrainCoordinates, x, z + 1, height, material);
    }
  };

  // Adapted from http://stevelosh.com/blog/2016/02/midpoint-displacement/
  var midpointDisplace = function(terrainCoordinates, jitterAmount, jitterDecay, top, right, bottom, left) {
    var topLeftHeight     = terrainCoordinates[top][left].height;
    var topRightHeight    = terrainCoordinates[top][right].height;
    var bottomLeftHeight  = terrainCoordinates[bottom][left].height;
    var bottomRightHeight = terrainCoordinates[bottom][right].height;

    var midY = top + ((bottom - top) / 2);
    var midX = left + ((right - left) / 2);

    var jitter;
    var newJitterAmount;
    var halfJitterAmount = jitterAmount / 2;

    // Left column
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[midY][left].height = ((topLeftHeight + bottomLeftHeight) / 2) + jitter;
    // Right column
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[midY][right].height = ((topRightHeight + bottomRightHeight) / 2) + jitter;
    // Top row
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[top][midX].height = ((topLeftHeight + topRightHeight) / 2) + jitter;
    // Bottom row
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[bottom][midX].height = ((bottomLeftHeight + bottomRightHeight) / 2) + jitter;

    // Middle
    var middleAverage = (terrainCoordinates[midY][left].height + terrainCoordinates[midY][right].height + terrainCoordinates[top][midX].height + terrainCoordinates[bottom][midX].height) / 4;
    terrainCoordinates[midY][midX].height = middleAverage;

    if ((midY - top) >= 2) {
      newJitterAmount = jitterAmount * jitterDecay;
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, top, midX, midY, left);
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, top, right, midY, midX);
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, midY, midX, bottom, left);
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, midY, right, bottom, midX);
    }
  };

  var terrainGenerator = {};

  terrainGenerator.generate = function(columns, rows) {
    var terrainCoordinates = buildTerrainCoordinates(columns, rows);
    return new CityTour.Terrain(terrainCoordinates);
  };

  return terrainGenerator;
})();
<commit_msg>Defining river as space between two curves<commit_after>"use strict";

var CityTour = CityTour || {};

CityTour.TerrainGenerator = (function() {
  var MAX_INITIAL_TERRAIN_HEIGHT = 6;
  var HEIGHT_JITTER_PER_ITERATION = 20;
  var HEIGHT_JITTER_DECAY_PER_ITERATION = 0.65;
  var LAND = 'land';
  var WATER = 'water';

  var emptyTerrain = function(columns, rows) {
    var x, z;
    var terrainCoordinates = [];

    for (x = 0; x <= columns; x++) {
      terrainCoordinates[x] = [];

      for (z = 0; z <= rows; z++) {
        terrainCoordinates[x][z] = { material: LAND, height: 0.0 };
      }
    }

    return terrainCoordinates;
  };

  var normalizeCoordinates = function(terrainCoordinates, columns, columnsToGenerate, rows, rowsToGenerate) {
    var x, z;
    var halfColumns = columns / 2;
    var halfRows = rows / 2;
    var columnOffset = halfColumns + ((columnsToGenerate - columns) / 2);
    var rowOffset = halfRows + ((rowsToGenerate - rows) / 2);
    var material;

    var normalizedTerrainCoordinates = [];

    for (x = -halfColumns; x <= halfColumns; x++) {
      normalizedTerrainCoordinates[x] = [];

      for (z = -halfRows; z <= halfRows; z++) {
        normalizedTerrainCoordinates[x][z] = {
          material: terrainCoordinates[x + columnOffset][z + rowOffset].material,
          height: terrainCoordinates[x + columnOffset][z + rowOffset].height,
        }
      }
    }

    return normalizedTerrainCoordinates;
  };

  var nextPowerOfTwo = function(n) {
    return Math.pow(2, Math.ceil(Math.log2(n)));
  };

  var buildTerrainCoordinates = function(columns, rows) {
    var columnsToGenerate = nextPowerOfTwo(columns);
    var rowsToGenerate = nextPowerOfTwo(rows);

    var terrainCoordinates = emptyTerrain(columnsToGenerate, rowsToGenerate);

    // Initial randomization of corners
    terrainCoordinates[0][0].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);
    terrainCoordinates[0][rows].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);
    terrainCoordinates[columns][0].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);
    terrainCoordinates[columns][rows].height = Math.floor(Math.random() * MAX_INITIAL_TERRAIN_HEIGHT);

    // City must be (2^n + 1) blocks on both x and z dimensions for this to work
    midpointDisplace(terrainCoordinates,
                     HEIGHT_JITTER_PER_ITERATION,
                     HEIGHT_JITTER_DECAY_PER_ITERATION,
                     0,
                     rowsToGenerate,
                     columnsToGenerate,
                     0);

    var x, z;

    var topCurve = new THREE.LineCurve(new THREE.Vector2(0, (rows / 2) + 6), new THREE.Vector2(columns, (rows / 2) + 3));
    var bottomCurve = new THREE.LineCurve(new THREE.Vector2(0, (rows / 2) + 10), new THREE.Vector2(columns, (rows / 2) + 20));

    var vector, topVector, bottomVector;
    var minimumRiverBankHeight = Number.POSITIVE_INFINITY;
    for (x = 0.0; x <= 1.0; x += 1 / columns) {
      vector = topCurve.getPoint(x);
      if (terrainCoordinates[vector.x][Math.round(vector.y)].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[vector.x][Math.round(vector.y)].height;
      }
    }
    for (x = 0.0; x <= 1.0; x += 1 / columns) {
      vector = bottomCurve.getPoint(x);
      console.log(vector.x + ", " + vector.y);
      if (terrainCoordinates[vector.x][Math.round(vector.y)].height < minimumRiverBankHeight) {
        minimumRiverBankHeight = terrainCoordinates[vector.x][Math.round(vector.y)].height;
      }
    }

    for (x = 0.0; x <= 1.0; x += 1 / columns) {
      topVector = topCurve.getPoint(x);
      bottomVector = bottomCurve.getPoint(x);

      for (z = Math.ceil(topVector.y); z < bottomVector.y; z++) {
        terrainCoordinates[topVector.x][z].height = minimumRiverBankHeight;
      }
    }

    floodFill(terrainCoordinates, 0, topCurve.getPoint(0).y, minimumRiverBankHeight, WATER);

    // Convert to final coordinates
    var finalTerrainCoordinates = normalizeCoordinates(terrainCoordinates, columns, columnsToGenerate, rows, rowsToGenerate);

    return finalTerrainCoordinates;
  };

  var floodFill = function(terrainCoordinates, x, z, height, material) {
    terrainCoordinates[x][z].height = height;
    terrainCoordinates[x][z].material = material;

    if (terrainCoordinates[x - 1] &&
        terrainCoordinates[x - 1][z] &&
        terrainCoordinates[x - 1][z].height <= height &&
        terrainCoordinates[x - 1][z].material != material) {
      floodFill(terrainCoordinates, x - 1, z, height, material);
    }
    if (terrainCoordinates[x + 1] &&
        terrainCoordinates[x + 1][z] &&
        terrainCoordinates[x + 1][z].height <= height &&
        terrainCoordinates[x + 1][z].material != material) {
      floodFill(terrainCoordinates, x + 1, z, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z - 1] &&
        terrainCoordinates[x][z - 1].height <= height &&
        terrainCoordinates[x][z - 1].material != material) {
      floodFill(terrainCoordinates, x, z - 1, height, material);
    }
    if (terrainCoordinates[x] &&
        terrainCoordinates[x][z + 1] &&
        terrainCoordinates[x][z + 1].height <= height &&
        terrainCoordinates[x][z + 1].material != material) {
      floodFill(terrainCoordinates, x, z + 1, height, material);
    }
  };

  // Adapted from http://stevelosh.com/blog/2016/02/midpoint-displacement/
  var midpointDisplace = function(terrainCoordinates, jitterAmount, jitterDecay, top, right, bottom, left) {
    var topLeftHeight     = terrainCoordinates[top][left].height;
    var topRightHeight    = terrainCoordinates[top][right].height;
    var bottomLeftHeight  = terrainCoordinates[bottom][left].height;
    var bottomRightHeight = terrainCoordinates[bottom][right].height;

    var midY = top + ((bottom - top) / 2);
    var midX = left + ((right - left) / 2);

    var jitter;
    var newJitterAmount;
    var halfJitterAmount = jitterAmount / 2;

    // Left column
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[midY][left].height = ((topLeftHeight + bottomLeftHeight) / 2) + jitter;
    // Right column
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[midY][right].height = ((topRightHeight + bottomRightHeight) / 2) + jitter;
    // Top row
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[top][midX].height = ((topLeftHeight + topRightHeight) / 2) + jitter;
    // Bottom row
    jitter = (Math.random() * jitterAmount) - halfJitterAmount;
    terrainCoordinates[bottom][midX].height = ((bottomLeftHeight + bottomRightHeight) / 2) + jitter;

    // Middle
    var middleAverage = (terrainCoordinates[midY][left].height + terrainCoordinates[midY][right].height + terrainCoordinates[top][midX].height + terrainCoordinates[bottom][midX].height) / 4;
    terrainCoordinates[midY][midX].height = middleAverage;

    if ((midY - top) >= 2) {
      newJitterAmount = jitterAmount * jitterDecay;
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, top, midX, midY, left);
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, top, right, midY, midX);
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, midY, midX, bottom, left);
      midpointDisplace(terrainCoordinates, newJitterAmount, jitterDecay, midY, right, bottom, midX);
    }
  };

  var terrainGenerator = {};

  terrainGenerator.generate = function(columns, rows) {
    var terrainCoordinates = buildTerrainCoordinates(columns, rows);
    return new CityTour.Terrain(terrainCoordinates);
  };

  return terrainGenerator;
})();
<|endoftext|>


<commit_before>var $ = require('jquery');
var _ = require('underscore');
var L = require('leaflet');
var MapView = require('../map-view');
var LeafletLayerViewFactory = require('./leaflet-layer-view-factory');

var LeafletMapView = MapView.extend({

  _createNativeMap: function () {
    var self = this;
    var center = this.map.get('center');

    var mapConfig = {
      zoomControl: false,
      center: new L.LatLng(center[0], center[1]),
      zoom: this.map.get('zoom'),
      minZoom: this.map.get('minZoom'),
      maxZoom: this.map.get('maxZoom'),
      dragging: !!this.map.get('drag'),
      doubleClickZoom: !!this.map.get('drag'),
      scrollWheelZoom: !!this.map.get('scrollwheel'),
      keyboard: !!this.map.get('keyboard'),
      attributionControl: false
    };

    this._leafletMap = new L.Map(this.el, mapConfig);

    this.map.bind('set_view', this._setView, this);

    this._leafletMap.on('layeradd', function (lyr) {
      this.trigger('layeradd', lyr, self);
    }, this);

    this._leafletMap.on('zoomstart', function () {
      self.trigger('zoomstart');
    });

    this._leafletMap.on('click', function (e) {
      self.trigger('click', e.originalEvent, [e.latlng.lat, e.latlng.lng]);
    });

    this._leafletMap.on('dblclick', function (e) {
      self.trigger('dblclick', e.originalEvent);
    });

    this._leafletMap.on('zoomend', function () {
      self._setModelProperty({
        zoom: self._leafletMap.getZoom()
      });
      self.trigger('zoomend');
    }, this);

    this._leafletMap.on('move', function () {
      var c = self._leafletMap.getCenter();
      self._setModelProperty({ center: [c.lat, c.lng] });
    });

    this._leafletMap.on('dragend', function () {
      var c = self._leafletMap.getCenter();
      this.trigger('dragend', [c.lat, c.lng]);
    }, this);

    this._leafletMap.on('moveend', function () {
      var c = self._leafletMap.getCenter();
      self.map.trigger('moveend', [c.lat, c.lng]);
    }, this);

    this._leafletMap.on('drag', function () {
      var c = self._leafletMap.getCenter();
      self._setModelProperty({
        center: [c.lat, c.lng]
      });
      self.trigger('drag');
    }, this);

    this.map.bind('change:maxZoom', function () {
      L.Util.setOptions(self._leafletMap, { maxZoom: self.map.get('maxZoom') });
    }, this);

    this.map.bind('change:minZoom', function () {
      L.Util.setOptions(self._leafletMap, { minZoom: self.map.get('minZoom') });
    }, this);
  },

  _getLayerViewFactory: function () {
    this._layerViewFactory = this._layerViewFactory || new LeafletLayerViewFactory({
      vector: this.map.get('vector'),
      webgl: this.map.get('webgl')
    });

    return this._layerViewFactory;
  },

  // this replaces the default functionality to search for
  // already added views so they are not replaced
  _addLayers: function (layerCollection, options) {
    var self = this;

    var oldLayers = this._layerViews;
    this._layerViews = {};

    function findLayerView (layer) {
      var lv = _.find(oldLayers, function (layerView) {
        var m = layerView.model;
        return m.isEqual(layer);
      });
      return lv;
    }

    function canReused (layer) {
      return self.map.layers.find(function (m) {
        return m.isEqual(layer);
      });
    }

    // remove all
    for (var layer in oldLayers) {
      var layerView = oldLayers[layer];
      if (!canReused(layerView.model)) {
        layerView.remove();
      }
    }

    this.map.layers.each(function (layerModel) {
      var layerView = findLayerView(layerModel);
      if (!layerView) {
        self._addLayer(layerModel, layerCollection, {
          silent: (options && options.silent) || false,
          index: options && options.index
        });
      } else {
        layerView.setModel(layerModel);
        self._layerViews[layerModel.cid] = layerView;
      }
    });
  },

  clean: function () {
    // see https://github.com/CloudMade/Leaflet/issues/1101
    L.DomEvent.off(window, 'resize', this._leafletMap._onResize, this._leafletMap);

    // destroy the map and clear all related event listeners
    this._leafletMap.remove();

    MapView.prototype.clean.call(this);
  },

  _setKeyboard: function (model, z) {
    if (z) {
      this._leafletMap.keyboard.enable();
    } else {
      this._leafletMap.keyboard.disable();
    }
  },

  _setScrollWheel: function (model, z) {
    if (z) {
      this._leafletMap.scrollWheelZoom.enable();
    } else {
      this._leafletMap.scrollWheelZoom.disable();
    }
  },

  _setZoom: function (model, z) {
    this._setView();
  },

  _setCenter: function (model, center) {
    this._setView();
  },

  _setView: function () {
    this._leafletMap.setView(this.map.get('center'), this.map.get('zoom') || 0);
  },

  _getNativeMap: function () {
    return this._leafletMap;
  },

  _addLayerToMap: function (layerView) {
    this._leafletMap.addLayer(layerView.leafletLayer);
    this._reorderLayerViews();
  },

  _reorderLayerViews: function () {
    this.map.layers.each(function (layerModel) {
      var layerView = this.getLayerViewByLayerCid(layerModel.cid);

      // CartoDBLayers share the same layerView so the zIndex is being overriden on every iteration.
      // The layerView will get the order of the last CartoDB layer as the zIndex
      if (layerView) {
        layerView.setZIndex(layerModel.get('order'));
      }
    }, this);
  },

  // return the current bounds of the map view
  getBounds: function () {
    var b = this._leafletMap.getBounds();
    var sw = b.getSouthWest();
    var ne = b.getNorthEast();
    return [
      [sw.lat, sw.lng],
      [ne.lat, ne.lng]
    ];
  },

  getSize: function () {
    return this._leafletMap.getSize();
  },

  panBy: function (p) {
    this._leafletMap.panBy(new L.Point(p.x, p.y));
  },

  setCursor: function (cursor) {
    $(this._leafletMap.getContainer()).css('cursor', cursor);
  },

  getNativeMap: function () {
    return this._leafletMap;
  },

  invalidateSize: function () {
    this._leafletMap.invalidateSize({ animate: false });
  },

  // GEOMETRY

  addMarker: function (marker) {
    marker.addToMap(this.getNativeMap());
  },

  removeMarker: function (marker) {
    marker.removeFromMap(this.getNativeMap());
  },

  hasMarker: function (marker) {
    return marker.isAddedToMap(this.getNativeMap());
  },

  addPath: function (path) {
    path.addToMap(this.getNativeMap());
  },

  removePath: function (path) {
    path.removeFromMap(this.getNativeMap());
  },

  latLngToContainerPoint: function (latlng) {
    var point = this.getNativeMap().latLngToContainerPoint(latlng);
    return {
      x: point.x,
      y: point.y
    };
  },

  // returns { lat: 0, lng: 0}
  containerPointToLatLng: function (point) {
    var latlng = this.getNativeMap().containerPointToLatLng([point.x, point.y]);
    return {
      lat: latlng.lat,
      lng: latlng.lng
    };
  }
});

// set the image path in order to be able to get leaflet icons
// code adapted from leaflet
L.Icon.Default.imagePath = (function () {
  var scripts = document.getElementsByTagName('script');
  var leafletRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/;

  var i, len, src, matches;

  for (i = 0, len = scripts.length; i < len; i++) {
    src = scripts[i].src;
    matches = src.match(leafletRe);

    if (matches) {
      var bits = src.split('/');
      delete bits[bits.length - 1];
      return bits.join('/') + 'themes/css/images';
    }
  }
}());

module.exports = LeafletMapView;
<commit_msg>Center properly map after invalidation size<commit_after>var $ = require('jquery');
var _ = require('underscore');
var L = require('leaflet');
var MapView = require('../map-view');
var LeafletLayerViewFactory = require('./leaflet-layer-view-factory');

var LeafletMapView = MapView.extend({

  _createNativeMap: function () {
    var self = this;
    var center = this.map.get('center');

    var mapConfig = {
      zoomControl: false,
      center: new L.LatLng(center[0], center[1]),
      zoom: this.map.get('zoom'),
      minZoom: this.map.get('minZoom'),
      maxZoom: this.map.get('maxZoom'),
      dragging: !!this.map.get('drag'),
      doubleClickZoom: !!this.map.get('drag'),
      scrollWheelZoom: !!this.map.get('scrollwheel'),
      keyboard: !!this.map.get('keyboard'),
      attributionControl: false
    };

    this._leafletMap = new L.Map(this.el, mapConfig);

    this.map.bind('set_view', this._setView, this);

    this._leafletMap.on('layeradd', function (lyr) {
      this.trigger('layeradd', lyr, self);
    }, this);

    this._leafletMap.on('zoomstart', function () {
      self.trigger('zoomstart');
    });

    this._leafletMap.on('click', function (e) {
      self.trigger('click', e.originalEvent, [e.latlng.lat, e.latlng.lng]);
    });

    this._leafletMap.on('dblclick', function (e) {
      self.trigger('dblclick', e.originalEvent);
    });

    this._leafletMap.on('zoomend', function () {
      self._setModelProperty({
        zoom: self._leafletMap.getZoom()
      });
      self.trigger('zoomend');
    }, this);

    this._leafletMap.on('move', function () {
      var c = self._leafletMap.getCenter();
      self._setModelProperty({ center: [c.lat, c.lng] });
    });

    this._leafletMap.on('dragend', function () {
      var c = self._leafletMap.getCenter();
      this.trigger('dragend', [c.lat, c.lng]);
    }, this);

    this._leafletMap.on('moveend', function () {
      var c = self._leafletMap.getCenter();
      self.map.trigger('moveend', [c.lat, c.lng]);
    }, this);

    this._leafletMap.on('drag', function () {
      var c = self._leafletMap.getCenter();
      self._setModelProperty({
        center: [c.lat, c.lng]
      });
      self.trigger('drag');
    }, this);

    this.map.bind('change:maxZoom', function () {
      L.Util.setOptions(self._leafletMap, { maxZoom: self.map.get('maxZoom') });
    }, this);

    this.map.bind('change:minZoom', function () {
      L.Util.setOptions(self._leafletMap, { minZoom: self.map.get('minZoom') });
    }, this);
  },

  _getLayerViewFactory: function () {
    this._layerViewFactory = this._layerViewFactory || new LeafletLayerViewFactory({
      vector: this.map.get('vector'),
      webgl: this.map.get('webgl')
    });

    return this._layerViewFactory;
  },

  // this replaces the default functionality to search for
  // already added views so they are not replaced
  _addLayers: function (layerCollection, options) {
    var self = this;

    var oldLayers = this._layerViews;
    this._layerViews = {};

    function findLayerView (layer) {
      var lv = _.find(oldLayers, function (layerView) {
        var m = layerView.model;
        return m.isEqual(layer);
      });
      return lv;
    }

    function canReused (layer) {
      return self.map.layers.find(function (m) {
        return m.isEqual(layer);
      });
    }

    // remove all
    for (var layer in oldLayers) {
      var layerView = oldLayers[layer];
      if (!canReused(layerView.model)) {
        layerView.remove();
      }
    }

    this.map.layers.each(function (layerModel) {
      var layerView = findLayerView(layerModel);
      if (!layerView) {
        self._addLayer(layerModel, layerCollection, {
          silent: (options && options.silent) || false,
          index: options && options.index
        });
      } else {
        layerView.setModel(layerModel);
        self._layerViews[layerModel.cid] = layerView;
      }
    });
  },

  clean: function () {
    // see https://github.com/CloudMade/Leaflet/issues/1101
    L.DomEvent.off(window, 'resize', this._leafletMap._onResize, this._leafletMap);

    // destroy the map and clear all related event listeners
    this._leafletMap.remove();

    MapView.prototype.clean.call(this);
  },

  _setKeyboard: function (model, z) {
    if (z) {
      this._leafletMap.keyboard.enable();
    } else {
      this._leafletMap.keyboard.disable();
    }
  },

  _setScrollWheel: function (model, z) {
    if (z) {
      this._leafletMap.scrollWheelZoom.enable();
    } else {
      this._leafletMap.scrollWheelZoom.disable();
    }
  },

  _setZoom: function (model, z) {
    this._setView();
  },

  _setCenter: function (model, center) {
    this._setView();
  },

  _setView: function () {
    this._leafletMap.setView(this.map.get('center'), this.map.get('zoom') || 0);
  },

  _getNativeMap: function () {
    return this._leafletMap;
  },

  _addLayerToMap: function (layerView) {
    this._leafletMap.addLayer(layerView.leafletLayer);
    this._reorderLayerViews();
  },

  _reorderLayerViews: function () {
    this.map.layers.each(function (layerModel) {
      var layerView = this.getLayerViewByLayerCid(layerModel.cid);

      // CartoDBLayers share the same layerView so the zIndex is being overriden on every iteration.
      // The layerView will get the order of the last CartoDB layer as the zIndex
      if (layerView) {
        layerView.setZIndex(layerModel.get('order'));
      }
    }, this);
  },

  // return the current bounds of the map view
  getBounds: function () {
    var b = this._leafletMap.getBounds();
    var sw = b.getSouthWest();
    var ne = b.getNorthEast();
    return [
      [sw.lat, sw.lng],
      [ne.lat, ne.lng]
    ];
  },

  getSize: function () {
    return this._leafletMap.getSize();
  },

  panBy: function (p) {
    this._leafletMap.panBy(new L.Point(p.x, p.y));
  },

  setCursor: function (cursor) {
    $(this._leafletMap.getContainer()).css('cursor', cursor);
  },

  getNativeMap: function () {
    return this._leafletMap;
  },

  invalidateSize: function () {
    var center = this.map.get('center');
    var zoom = this.map.get('zoom');
    this._leafletMap.invalidateSize({ pan: false, animate: false });
    this._leafletMap.setView(center, zoom, { pan: false, animate: false });
  },

  // GEOMETRY

  addMarker: function (marker) {
    marker.addToMap(this.getNativeMap());
  },

  removeMarker: function (marker) {
    marker.removeFromMap(this.getNativeMap());
  },

  hasMarker: function (marker) {
    return marker.isAddedToMap(this.getNativeMap());
  },

  addPath: function (path) {
    path.addToMap(this.getNativeMap());
  },

  removePath: function (path) {
    path.removeFromMap(this.getNativeMap());
  },

  latLngToContainerPoint: function (latlng) {
    var point = this.getNativeMap().latLngToContainerPoint(latlng);
    return {
      x: point.x,
      y: point.y
    };
  },

  // returns { lat: 0, lng: 0}
  containerPointToLatLng: function (point) {
    var latlng = this.getNativeMap().containerPointToLatLng([point.x, point.y]);
    return {
      lat: latlng.lat,
      lng: latlng.lng
    };
  }
});

// set the image path in order to be able to get leaflet icons
// code adapted from leaflet
L.Icon.Default.imagePath = (function () {
  var scripts = document.getElementsByTagName('script');
  var leafletRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/;

  var i, len, src, matches;

  for (i = 0, len = scripts.length; i < len; i++) {
    src = scripts[i].src;
    matches = src.match(leafletRe);

    if (matches) {
      var bits = src.split('/');
      delete bits[bits.length - 1];
      return bits.join('/') + 'themes/css/images';
    }
  }
}());

module.exports = LeafletMapView;
<|endoftext|>


<commit_before>const downloadObjectAsJson = (exportObj, exportName) => {
    const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(exportObj))
    let downloadAnchorNode = document.createElement('a')

    downloadAnchorNode.setAttribute('href', dataStr)
    downloadAnchorNode.setAttribute('download', exportName + '.json')
    downloadAnchorNode.click()
    downloadAnchorNode.remove()
}

export default downloadObjectAsJson<commit_msg>Copy and download is implemented on the list and edit pages<commit_after>const downloadObjectAsJson = (exportObj, exportName) => {
  const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(exportObj))
  let downloadAnchorNode = document.createElement('a')

  downloadAnchorNode.setAttribute('href', dataStr)
  downloadAnchorNode.setAttribute('download', exportName + '.json')
  downloadAnchorNode.click()
  downloadAnchorNode.remove()
}

export default downloadObjectAsJson
<|endoftext|>


<commit_before>/*
 *  Event Based Animation v1.0 - jQuery plugin
 *
 *  Copyright (c) 2012 Deux Huit Huit (http://www.deuxhuithuit.com/)
 *  Licensed under the MIT (https://raw.github.com/DeuxHuitHuit/jQuery-event-based-animation/master/LICENSE.txt)
 */
(function ($, undefined) {

	"use strict";

	if (!$.isFunction($.isString)) {
		$.isString = function (object) {
			return $.type(object) === 'string';
		};
	}

	$.fn.eventAnimate = function (options) {

		var

		// the target DOMElement
		t = $(this),

		// Window object
		win = $(window),

		// Quick timestamp
		now = $.now,

		// Safe division
		sdiv = function (n, d) {
			if (!n || !d) {
				return 0;
			}
			return n/d;
		},

		// Quick iterator
		_forEach = function (o, iterator) {
			$.each(o.properties, iterator);
		},

		// Quick setter
		_setEach = function (o, object, values) {
			_forEach(o, function (index, key) {
				object[key] = $.isFunction(values) ?
								values(key) :
								($.isArray(values) ? values[key] : values);
			});
		},

		// Last event triggered
		eventTimeStamp = now(),

		// Last animation frame
		lastAnimatedTimeStamp = eventTimeStamp,

		// Current running animation duration
		currentAnimationDuration = {},

		// Current animation start values
		currentStartAnimationPosition = {},

		// Save the target position (updated by the event)
		targetPosition = {},

		// Save the distance between current position and the target (Refreshed at every tick)
		targetDistance = {},

		// Save the current ghost position (Refreshed when tick apply animation)
		currentPosition = {},

		_initVariables = function (o) {
			_setEach(o, currentAnimationDuration, 0);
			_setEach(o, currentStartAnimationPosition, 0);
			_setEach(o, targetPosition, 0);
			_setEach(o, targetDistance, 0);
			_setEach(o, currentPosition, 0);
		},

		_setTimeout = function (fx, delay) {
			var w = window,
				frm = w.requestAnimationFrame || w.mozRequestAnimationFrame ||
					w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
					w.oRequestAnimationFrame || w.setTimeout;

			return frm(fx, frm === w.setTimeout ? delay : o.container.get(0));
		},

		_clearTimeout = function (timeout) {
			var w = window,
				frm = w.cancelAnimationFrame || w.webkitCancelRequestAnimationFrame ||
					w.mozCancelRequestAnimationFrame || w.oCancelRequestAnimationFrame ||
					w.msCancelRequestAnimationFrame  || w.clearTimeout;

			return frm(timeout);
		},

		//Reference the current timer
		timer = null,

		isMoving = false,

		defaultOffset = {left:0,top:0},

		_handleScroll = function (e, o, targetPosition) {
			targetPosition.x = o.container.scrollLeft();
			targetPosition.y = o.container.scrollTop();
		},

		_handleMouse = function (e, o, targetPosition) {
			var offset = o.container.offset() || defaultOffset;
			targetPosition.x = e.pageX - offset.left;
			targetPosition.y = e.pageY - offset.top;
		},

		_handleTouch = function (e, o, targetPosition) {
			var offset = o.container.offset() || defaultOffset,
				touch = e.originalEvent.touches[0];
			targetPosition.x = touch.pageX - offset.left;
			targetPosition.y = touch.pageY - offset.top;
		},

		// Event Strategies
		_eventStrategies = {
			scroll: _handleScroll,
			click: _handleMouse,
			mouseover: _handleMouse,
			mousemove: _handleMouse,
			touchmove: _handleTouch
		},

		// Handle the container event
		_handleEvent = function (e) {
			var
			eventName = e.type || o.event,
			strategy = _eventStrategies[eventName];

			if ($.isFunction(strategy)) {
				// Call the strategy
				strategy.call(t, e, o, targetPosition);

				if (!!o.restartOnEvent) {
					// Update the event time
					eventTimeStamp = now()-1; // make it in the past
				}

				if (!isMoving) {

					// Only schedule for frame
					if (!!o.delayStart) {
						startTimer();
					}
					// Start the animation right now
					else {
						_nextFrame();
					}
				}

			} else if (!!window.console) {
				console.err('No strategy found for event "' + eventName + '"');
			}
		},

		//Start a new timer
		startTimer = function () {
			if (!_checkStop(o)) {
				timer = _setTimeout(_nextFrame, o.tick);
			}
			// animation stopped, so no timer is allowed
			else {
				_clearTimeout(timer);
				timer = null;
			}
			return timer;
		},

		// parses the duration options
		_getDuration = function(o, targetDistance, startValues, targetValues) {
			if (!$.isFunction(o.duration)) {
				return {
					x: (o.duration.x || o.duration || Math.abs(targetDistance.x)) * o.durationRatio,
					y: (o.duration.y || o.duration || Math.abs(targetDistance.y)) * o.durationRatio
				};
			} else {
				var result = o.duration.call(t, o, targetDistance);
				return {
					x: Math.abs(result.x || result || targetDistance.x) * o.durationRatio,
					y: Math.abs(result.y || result || targetDistance.y) * o.durationRatio
				};
			}
		},

		// parses the startValue options
		_getStartValues = function (o) {
			var startValues = null;
			if ($.isFunction(o.startValues)) {
				startValues = o.startValues(o);
			}
			if (!startValues) {
				startValues = currentPosition;
			}
			return startValues;
		},

		// Calculate the first "legacy" easing parameter
		getLegacyEasingValue = function (currentAnimationTime) {
			return {
				x: currentStartAnimationPosition.x + (Math.min(1, sdiv(currentAnimationTime, currentAnimationDuration.x)) * (targetPosition.x-currentStartAnimationPosition.x)),
				y: currentStartAnimationPosition.y + (Math.min(1, sdiv(currentAnimationTime, currentAnimationDuration.y)) * (targetPosition.y-currentStartAnimationPosition.y))
			};
		},

		// Allow to stop the animation
		_checkStop = function (o) {
			if (!$.isFunction(o.stop)) {
				return false;
			}
			return o.stop(o);
		},

		// Called at every tick
		_nextFrame = function () {

			if(!_checkStop(o)) {

				// save travel distance needed
				targetDistance.x = targetPosition.x - currentPosition.x;
				targetDistance.y = targetPosition.y - currentPosition.y;

				// animation : Do we have something to travel
				if (targetDistance.x !== 0 || targetDistance.y !== 0) {

					// if the value changed since lass pass,
					// i.e. an event occur between two frames
					// or before the current one
					if (eventTimeStamp > lastAnimatedTimeStamp) {

						// Save the start point of the animation
						var startValues = _getStartValues(o);

						// Update target distances
						targetDistance.x = targetPosition.x - startValues.x;
						targetDistance.y = targetPosition.y - startValues.y;

						// Update current duration
						currentAnimationDuration = _getDuration(o, targetDistance, startValues, targetPosition);

						// Set start as current
						currentStartAnimationPosition = startValues;

						// Set Last Animated Time Stamp to the new scroll Event Time Stamp
						// This acts as the new animation start
						if (!isMoving || !!o.restartOnEvent) {
							lastAnimatedTimeStamp = now();
						}
					} // if scrolled

					// Begin Var
					var

					// Current animation time (where are we rith now)
					currentAnimationTime = now() - lastAnimatedTimeStamp,

					// Linear/swing algorigthm
					linearPosition = getLegacyEasingValue(currentAnimationTime),
					easingFx = o.easing || $.easing.def || 'linear',
					easingCurPosition = {
						// We documented the parameter names and logic, since there is an error on
						// the main doc: http://gsgd.co.uk/sandbox/jquery/easing/. end_value is actually a diff.
						//                    old_fx,           current_time,                                               start_value,                     end_value-start_value,                             duration
						x: $.easing[easingFx](linearPosition.x, Math.min(currentAnimationTime, currentAnimationDuration.x), currentStartAnimationPosition.x, targetPosition.x-currentStartAnimationPosition.x, currentAnimationDuration.x),
						y: $.easing[easingFx](linearPosition.y, Math.min(currentAnimationTime, currentAnimationDuration.y), currentStartAnimationPosition.y, targetPosition.y-currentStartAnimationPosition.y, currentAnimationDuration.y)
					};

					// end var

					if (!!window.console && !!o.debug) {
						if (o.debug === true || o.debug.x === true) {
							console.log(lastAnimatedTimeStamp +
									' Target X ' + parseInt(targetPosition.x,10) +
									' - Start ' + parseInt(currentStartAnimationPosition.x,10) +
									' - Linear ' + parseInt(linearPosition.x,10) +
									' - Eased ' + parseInt(easingCurPosition.x,10) +
									' - Time ' + Math.min(currentAnimationTime, currentAnimationDuration.x) +
									' - Duration ' + currentAnimationDuration.x);
						}
						if (o.debug === true || o.debug.y === true) {
							console.log(lastAnimatedTimeStamp +
									' Target Y ' + parseInt(targetPosition.y,10) +
									' - Start ' + parseInt(currentStartAnimationPosition.y,10) +
									' - Linear ' + parseInt(linearPosition.y,10) +
									' - Eased ' + parseInt(easingCurPosition.y,10) +
									' - Time ' + Math.min(currentAnimationTime, currentAnimationDuration.y) +
									' - Duration ' + currentAnimationDuration.y);
						}
					}

					if ($.isNumeric(easingCurPosition.x) && $.isNumeric(easingCurPosition.y)) {
						// update currentPosition state
						currentPosition = easingCurPosition;
					}

					// Start Callback
					if (!isMoving && $.isFunction(o.start)) {
						o.start.call(t, currentAnimationTime, currentPosition);
					}

					// Assure moving flag is on
					isMoving = true;

					// if we still have time left on the animation
					if (currentAnimationTime < currentAnimationDuration.x ||
						currentAnimationTime < currentAnimationDuration.y) {
						// queue next frame
						startTimer();
					} else {
						// animation stopped
						timer = null;
						isMoving = false;
					}

					// call callback with new ghost position
					if ($.isFunction(o.step)) {
						o.step.call(t, currentAnimationTime, currentPosition);
					}

					// End Callback
					if (timer === null) {
						if ($.isFunction(o.complete)) {
							o.complete.call(t, currentAnimationTime, currentPosition);
						}
						// reset if we have to
						currentPosition = _getStartValues(o);
					}

				}
				// no distance found
				else {
					// animation won't run
					timer = null;
					isMoving = false;
				}
			}
			// animation stopped
			else {
				// we stop, no timer is needed
				timer = null;
				isMoving = false;
			}
		},

		// assure minimal option object
		o = $.extend({
			container: null, // The DOMElement where to listen the event. Target if omitted.
			tick: 16, // Default timeout when requestAnimationFrame is not available. In ms.
			event: 'scroll', // The event(s) to listen for changes
			properties: 'x y', // The properties that we are animating (array or string)
			delayStart: false, // Make the event schedule next frame instead of calling it
			duration: 0, // Both axis animation duration. Numeric, object (x:1,y:1} or function
			durationRatio: 1, // Duration modifier. Particularly usefull when duration depends on distance
			restartOnEvent: false, // creates an absolute start time instead of relative to the last event
			stop: null, // A stop function to stop the animation. Your logic, your rules.
			step: null, // A function to call at each step of the animation.
			complete: null, // A callback function called when the animation ends.
			start: null, // A callback function called when the animation begins.
			easing: null, // A easing function to use. $.easing.def or linear if omitted.
			strategy: null, // A strategy function for your custom event.
			startValues: null, // A function that permits override of the stating values
			debug: false // Set to true to get extra data in the console. Can be set per axis {x:false, y:true}
		}, options);

		// If not container is set, use the target
		o.container = $(o.container || t);

		// Add the new strategy if needed
		if ($.isFunction(o.strategy)) {
			_eventStrategies[o.event] = o.strategy;
		}
		// If strategy is a string, try code re-use
		else if ($.isString(o.strategy)) {
			_eventStrategies[o.event] = _eventStrategies[o.strategy];
		}
		// If strategy is an object, add multiple events
		else if ($.isPlainObject(o.strategy)) {
			$.each(o.strategy, function _importStrategy(key, s) {
				_eventStrategies[key] = s;
			});
		}

		// Split into array if it is a string
		if ($.isString(o.properties)) {
			o.properties = o.properties.split(' ');
		}

		// If no animatable properties have been found
		if (!$.isArray(o.properties) || o.properties.length < 1) {
			// Cannot continue
			return t;
		}

		// Init "globals"
		_initVariables(o);

		// Hook up on event
		o.container.on(o.event, _handleEvent);

		// Always return jQuery object
		return t;

	}; // end $.fn.extend
})(jQuery);
<commit_msg>debug and duration now supports multiple properties<commit_after>/*
 *  Event Based Animation v1.0 - jQuery plugin
 *
 *  Copyright (c) 2012 Deux Huit Huit (http://www.deuxhuithuit.com/)
 *  Licensed under the MIT (https://raw.github.com/DeuxHuitHuit/jQuery-event-based-animation/master/LICENSE.txt)
 */
(function ($, undefined) {

	"use strict";

	if (!$.isFunction($.isString)) {
		$.isString = function (object) {
			return $.type(object) === 'string';
		};
	}

	$.fn.eventAnimate = function (options) {

		var

		// the target DOMElement
		t = $(this),

		// Window object
		win = $(window),

		// Quick timestamp
		now = $.now,

		// Safe division
		sdiv = function (n, d) {
			if (!n || !d) {
				return 0;
			}
			return n/d;
		},

		// Quick iterator
		_forEach = function (o, iterator) {
			$.each(o.properties, iterator);
		},

		// Quick setter
		_setEach = function (o, object, values) {
			_forEach(o, function (index, key) {
				object[key] = $.isFunction(values) ?
								values(key) :
								($.isArray(values) ? values[key] : values);
			});
		},

		// Last event triggered
		eventTimeStamp = now(),

		// Last animation frame
		lastAnimatedTimeStamp = eventTimeStamp,

		// Current running animation duration
		currentAnimationDuration = {},

		// Current animation start values
		currentStartAnimationPosition = {},

		// Save the target position (updated by the event)
		targetPosition = {},

		// Save the distance between current position and the target (Refreshed at every tick)
		targetDistance = {},

		// Save the current ghost position (Refreshed when tick apply animation)
		currentPosition = {},

		_initVariables = function (o) {
			_setEach(o, currentAnimationDuration, 0);
			_setEach(o, currentStartAnimationPosition, 0);
			_setEach(o, targetPosition, 0);
			_setEach(o, targetDistance, 0);
			_setEach(o, currentPosition, 0);
		},

		_setTimeout = function (fx, delay) {
			var w = window,
				frm = w.requestAnimationFrame || w.mozRequestAnimationFrame ||
					w.webkitRequestAnimationFrame || w.msRequestAnimationFrame ||
					w.oRequestAnimationFrame || w.setTimeout;

			return frm(fx, frm === w.setTimeout ? delay : o.container.get(0));
		},

		_clearTimeout = function (timeout) {
			var w = window,
				frm = w.cancelAnimationFrame || w.webkitCancelRequestAnimationFrame ||
					w.mozCancelRequestAnimationFrame || w.oCancelRequestAnimationFrame ||
					w.msCancelRequestAnimationFrame  || w.clearTimeout;

			return frm(timeout);
		},

		//Reference the current timer
		timer = null,

		isMoving = false,

		defaultOffset = {left:0,top:0},

		// Event Strategies implementation
		_handleScroll = function (e, o, targetPosition) {
			targetPosition.x = o.container.scrollLeft();
			targetPosition.y = o.container.scrollTop();
		},

		_handleMouse = function (e, o, targetPosition) {
			var offset = o.container.offset() || defaultOffset;
			targetPosition.x = e.pageX - offset.left;
			targetPosition.y = e.pageY - offset.top;
		},

		_handleTouch = function (e, o, targetPosition) {
			var offset = o.container.offset() || defaultOffset,
				touch = e.originalEvent.touches[0];
			targetPosition.x = touch.pageX - offset.left;
			targetPosition.y = touch.pageY - offset.top;
		},

		// Defaults Event Strategies
		_eventStrategies = {
			scroll: _handleScroll,
			click: _handleMouse,
			mouseover: _handleMouse,
			mousemove: _handleMouse,
			touchmove: _handleTouch
		},

		// Handle the container event
		_handleEvent = function (e) {
			var
			eventName = e.type || o.event,
			strategy = _eventStrategies[eventName];

			if ($.isFunction(strategy)) {
				// Call the strategy
				strategy.call(t, e, o, targetPosition);

				// Update the event time
				if (!isMoving || !!o.restartOnEvent) {
					eventTimeStamp = now()-1; // make it in the past
				}

				//  Schedule frame if not moving
				if (!isMoving) {

					// Only schedule for frame
					if (!!o.delayStart) {
						startTimer();
					}
					// Start the animation right now
					else {
						_nextFrame();
					}
				}

			} else if (!!window.console) {
				console.err('No strategy found for event "' + eventName + '"');
			}
		},

		// Start a new timer
		startTimer = function () {
			if (!_checkStop(o)) {
				timer = _setTimeout(_nextFrame, o.tick);
			}
			// animation stopped, so no timer is allowed
			else {
				_clearTimeout(timer);
				timer = null;
			}
			return timer;
		},

		// Parses the duration options
		_getDuration = function(o, targetDistance, startValues, targetValues) {
			var r = {};
			if ($.isFunction(o.duration)) {
				var result = o.duration.call(t, o, targetDistance);
				_setEach(o, r, function (key) {
					return Math.abs(result[key] || result || targetDistance[key]) * o.durationRatio;
				});
			} else {
				_setEach(o, r, function (key) {
					return Math.abs(o.duration[key] || o.duration || targetDistance[key]) * o.durationRatio;
				});
			}
			return r;
		},

		// parses the startValue options
		_getStartValues = function (o) {
			var startValues = null;
			if ($.isFunction(o.startValues)) {
				startValues = o.startValues(o);
			}
			if (!startValues) {
				startValues = currentPosition;
			}
			return startValues;
		},

		// Calculate the first "legacy" easing parameter
		getLegacyEasingValue = function (currentAnimationTime) {
			return {
				x: currentStartAnimationPosition.x + (Math.min(1, sdiv(currentAnimationTime, currentAnimationDuration.x)) * (targetPosition.x-currentStartAnimationPosition.x)),
				y: currentStartAnimationPosition.y + (Math.min(1, sdiv(currentAnimationTime, currentAnimationDuration.y)) * (targetPosition.y-currentStartAnimationPosition.y))
			};
		},

		_debugOutput = function (o, linearPosition, easingCurPosition, currentAnimationTime) {
			if (!!window.console && console.log && !!o.debug) {
				_forEach(o, function (index, key) {
					if (o.debug === true || o.debug[key] === true) {
						console.log(lastAnimatedTimeStamp +
								' Target ' + key + ' ' + parseInt(targetPosition[key],10) +
								' - Start ' + parseInt(currentStartAnimationPosition[key],10) +
								' - Linear ' + parseInt(linearPosition[key],10) +
								' - Eased ' + parseInt(easingCurPosition[key],10) +
								' - Time ' + Math.min(currentAnimationTime, currentAnimationDuration[key]) +
								' - Duration ' + currentAnimationDuration[key]);
					}
				});
			}
		},

		// Allow to stop the animation
		_checkStop = function (o) {
			if (!$.isFunction(o.stop)) {
				return false;
			}
			return o.stop(o);
		},

		// Called at every tick
		_nextFrame = function () {

			if(!_checkStop(o)) {

				// save travel distance needed
				targetDistance.x = targetPosition.x - currentPosition.x;
				targetDistance.y = targetPosition.y - currentPosition.y;

				// animation : Do we have something to travel
				if (targetDistance.x !== 0 || targetDistance.y !== 0) {

					// if the value changed since lass pass,
					// i.e. an event occur between two frames
					// or before the current one
					if (eventTimeStamp > lastAnimatedTimeStamp) {

						// Save the start point of the animation
						var startValues = _getStartValues(o);

						// Update target distances
						targetDistance.x = targetPosition.x - startValues.x;
						targetDistance.y = targetPosition.y - startValues.y;

						// Update current duration
						currentAnimationDuration = _getDuration(o, targetDistance, startValues, targetPosition);

						// Set start as current
						currentStartAnimationPosition = startValues;

						// Set Last Animated Time Stamp to the new scroll Event Time Stamp
						// This acts as the new animation start
						if (!isMoving || !!o.restartOnEvent) {
							lastAnimatedTimeStamp = now();
						}
					} // if scrolled

					// Begin Var
					var

					// Current animation time (where are we rith now)
					currentAnimationTime = now() - lastAnimatedTimeStamp,

					// Linear/swing algorigthm
					linearPosition = getLegacyEasingValue(currentAnimationTime),
					easingFx = o.easing || $.easing.def || 'linear',
					easingCurPosition = {
						// We documented the parameter names and logic, since there is an error on
						// the main doc: http://gsgd.co.uk/sandbox/jquery/easing/. end_value is actually a diff.
						//                    old_fx,           current_time,                                               start_value,                     end_value-start_value,                             duration
						x: $.easing[easingFx](linearPosition.x, Math.min(currentAnimationTime, currentAnimationDuration.x), currentStartAnimationPosition.x, targetPosition.x-currentStartAnimationPosition.x, currentAnimationDuration.x),
						y: $.easing[easingFx](linearPosition.y, Math.min(currentAnimationTime, currentAnimationDuration.y), currentStartAnimationPosition.y, targetPosition.y-currentStartAnimationPosition.y, currentAnimationDuration.y)
					};

					// end var

					// DEBUG
					_debugOutput(o, linearPosition, easingCurPosition, currentAnimationTime);

					if ($.isNumeric(easingCurPosition.x) && $.isNumeric(easingCurPosition.y)) {
						// update currentPosition state
						currentPosition = easingCurPosition;
					}

					// Start Callback
					if (!isMoving && $.isFunction(o.start)) {
						o.start.call(t, currentAnimationTime, currentPosition);
					}

					// Assure moving flag is on
					isMoving = true;

					// if we still have time left on the animation
					if (currentAnimationTime < currentAnimationDuration.x ||
						currentAnimationTime < currentAnimationDuration.y) {
						// queue next frame
						startTimer();
					} else {
						// animation stopped
						timer = null;
						isMoving = false;
					}

					// call callback with new ghost position
					if ($.isFunction(o.step)) {
						o.step.call(t, currentAnimationTime, currentPosition);
					}

					// End Callback
					if (timer === null) {
						if ($.isFunction(o.complete)) {
							o.complete.call(t, currentAnimationTime, currentPosition);
						}
						// reset if we have to
						currentPosition = _getStartValues(o);
					}

				}
				// no distance found
				else {
					// animation won't run
					timer = null;
					isMoving = false;
				}
			}
			// animation stopped
			else {
				// we stop, no timer is needed
				timer = null;
				isMoving = false;
			}
		},

		// assure minimal option object
		o = $.extend({
			container: null, // The DOMElement where to listen the event. Target if omitted.
			tick: 16, // Default timeout when requestAnimationFrame is not available. In ms.
			event: 'scroll', // The event(s) to listen for changes
			properties: 'x y', // The properties that we are animating (array or string)
			delayStart: false, // Make the event schedule next frame instead of calling it
			duration: 0, // Both axis animation duration. Numeric, object (x:1,y:1} or function
			durationRatio: 1, // Duration modifier. Particularly usefull when duration depends on distance
			restartOnEvent: false, // creates an absolute start time instead of relative to the last event
			stop: null, // A stop function to stop the animation. Your logic, your rules.
			step: null, // A function to call at each step of the animation.
			complete: null, // A callback function called when the animation ends.
			start: null, // A callback function called when the animation begins.
			easing: null, // A easing function to use. $.easing.def or linear if omitted.
			strategy: null, // A strategy function for your custom event.
			startValues: null, // A function that permits override of the stating values
			debug: false // Set to true to get extra data in the console. Can be set per axis {x:false, y:true}
		}, options);

		// If not container is set, use the target
		o.container = $(o.container || t);

		// Add the new strategy if needed
		if ($.isFunction(o.strategy)) {
			_eventStrategies[o.event] = o.strategy;
		}
		// If strategy is a string, try code re-use
		else if ($.isString(o.strategy)) {
			_eventStrategies[o.event] = _eventStrategies[o.strategy];
		}
		// If strategy is an object, add multiple events
		else if ($.isPlainObject(o.strategy)) {
			$.each(o.strategy, function _importStrategy(key, s) {
				_eventStrategies[key] = s;
			});
		}

		// Split into array if it is a string
		if ($.isString(o.properties)) {
			o.properties = o.properties.split(' ');
		}

		// If no animatable properties have been found
		if (!$.isArray(o.properties) || o.properties.length < 1) {
			// Cannot continue
			return t;
		}

		// Init "globals"
		_initVariables(o);

		// Hook up on event
		o.container.on(o.event, _handleEvent);

		// Always return jQuery object
		return t;

	}; // end $.fn.extend
})(jQuery);
<|endoftext|>


<commit_before>'use strict';

define(['backend/port', 'util/protocol'], function(Port, Protocol) {

  /******************
   * Delivery tasks
   * {
   *     id:   <destination idkey>
   *     msg:  <json message>
   *     sent: <timestamp>
   *     ack:  <timestamp>
   **/

  /**
   * Service tracking and sending messages to peers
   * @constructor
   */
  function DeliveryService(core) {
    var self = this;
    this.name = 'delivery';
    this.core = core;
    this.ongoing = {};
    this.messages = [];

    // Connect to wallet port for identity cleanup and startup
    Port.connect('wallet', function(data) {
      // Cleanup on identity change;
      if (data.type === 'ready') {
        self.onIdentityStart();
      }
      else if (data.type === 'closing') {
        self.ongoing = {};
        self.messages = [];
      }
    });

    // Connect to contacts port to know about contacts becoming available
    Port.connect('contacts', function(data) {
        if (data.type === 'contact') {
            self.onContactAvailable(data.peer);
        }
    });

    // Connect to channel port to receive acks
    Port.connect('channel', function(data) {
        if (data.type === 'initChannel') {
            console.log("[delivery] connecting", data.name);
            var channel = core.getLobbyTransport().getChannel(data.name);
            channel.addCallback('Ack', function(msg) { self.onAck(msg); } );
        }
    });

  }

  /**
   * Send message
   */
  DeliveryService.prototype.sendMessage = function(contact, message) {
      this.messages.push({id: contact.findIdentityKey().data, msg: message});
  };

  /**
   * Identity is starting
   */
  DeliveryService.prototype.onIdentityStart = function() {
      console.log("[delivery] starting identity...");
      var identity = this.core.getCurrentIdentity();
      // Initialize delivery tasks on identity
      if (!identity.tasks.tasks.hasOwnProperty('delivery')) {
          identity.tasks.tasks['delivery'] = [];
      }
      this.messages = identity.tasks.tasks['delivery'];
  };

  /**
   * Check for peer messages
   */
  DeliveryService.prototype.checkPeerMessages = function(peer, idKey) {
      console.log("[delivery] contact available", peer.contact.data.name, idKey.data);
      var self = this;

      // Iterate over tasks
      this.messages.forEach(function(element) {
          if (!element.ack && idKey && element.id === idKey) {
              self.send(peer, element);
          }
      });
  };

  /**
   * Send a task to a contact
   * @private
   */
  DeliveryService.prototype.send = function(peer, task) {
      var msg = task.msg;
      task.sent = Date.now();
      if (msg && msg.body) {
          // keep track of sent until ack arrives
          msg.body.id = Math.random();
          this.ongoing[msg.body.id] = task;
      }

      peer.channel.postDH(peer.pubKey, msg, function() {});
  };

  /**
   * A contact becomes available.
   */
  DeliveryService.prototype.onContactAvailable = function(peer) {
      // See if we have anything to send to this contact
      var idKey = peer.contact.findIdentityKey();
      if (idKey) {
          this.checkPeerMessages(peer, idKey);
      }
  };

  /**
   * Acknowledge receiving a spend request
   * @private
   */
  DeliveryService.prototype.sendAck = function(peer, body) {
      // Add the task
      var msg = Protocol.AckMsg(body.id);
      peer.channel.postDH(peer.pubKey, msg, function() {});
  };

  /**
   * A new multisig ack has arrived
   */
  DeliveryService.prototype.onAck = function(msg) {
      var peer = msg.peer;
      var tracking = this.ongoing[msg.body.id];
      if (tracking) {
          tracking.ack = Date.now();
          delete this.ongoing[msg.body.id];
      }
  };

  return DeliveryService;

});
<commit_msg>Add test message for debugging.<commit_after>'use strict';

define(['backend/port', 'util/protocol'], function(Port, Protocol) {

  /******************
   * Delivery tasks
   * {
   *     id:   <destination idkey>
   *     msg:  <json message>
   *     sent: <timestamp>
   *     ack:  <timestamp>
   **/

  /**
   * Service tracking and sending messages to peers
   * @constructor
   */
  function DeliveryService(core) {
    var self = this;
    this.name = 'delivery';
    this.core = core;
    this.ongoing = {};
    this.messages = [];

    // Connect to wallet port for identity cleanup and startup
    Port.connect('wallet', function(data) {
      // Cleanup on identity change;
      if (data.type === 'ready') {
        self.onIdentityStart();
      }
      else if (data.type === 'closing') {
        self.ongoing = {};
        self.messages = [];
      }
    });

    // Connect to contacts port to know about contacts becoming available
    Port.connect('contacts', function(data) {
        if (data.type === 'contact') {
            self.onContactAvailable(data.peer);
        }
    });

    // Connect to channel port to receive acks
    Port.connect('channel', function(data) {
        if (data.type === 'initChannel') {
            console.log("[delivery] connecting", data.name);
            var channel = core.getLobbyTransport().getChannel(data.name);
            channel.addCallback('Ack', function(msg) { self.onAck(msg); } );
            channel.addCallback('DeliveryTest', function(msg) { self.onDeliveryTest(msg); } );
        }
    });

  }

  /**
   * Send message
   */
  DeliveryService.prototype.sendMessage = function(contact, message) {
      var newTask = {id: contact.findIdentityKey().data, msg: message};
      this.messages.push(newTask);
      if (contact.online) {
          this.send(contact.online, newTask);
      }
  };

  /**
   * Identity is starting
   */
  DeliveryService.prototype.onIdentityStart = function() {
      console.log("[delivery] starting identity...");
      var identity = this.core.getCurrentIdentity();
      // Initialize delivery tasks on identity
      if (!identity.tasks.tasks.hasOwnProperty('delivery')) {
          identity.tasks.tasks['delivery'] = [];
      }
      this.messages = identity.tasks.tasks['delivery'];
  };

  /**
   * A contact becomes available.
   */
  DeliveryService.prototype.onContactAvailable = function(peer) {
      // See if we have anything to send to this contact
      var idKey = peer.contact.findIdentityKey();
      if (idKey) {
          this.checkPeerMessages(peer, idKey);
      }
  };

  /**
   * Check for peer messages
   */
  DeliveryService.prototype.checkPeerMessages = function(peer, idKey) {
      console.log("[delivery] contact available", peer.contact.data.name, idKey.data);
      var self = this;

      // Iterate over tasks
      this.messages.forEach(function(element) {
          if (!element.ack && idKey && element.id === idKey) {
              self.send(peer, element);
          }
      });
  };

  /**
   * Send a task to a contact
   * @private
   */
  DeliveryService.prototype.send = function(peer, task) {
      var msg = task.msg;
      task.sent = Date.now();
      if (msg && msg.body) {
          // keep track of sent until ack arrives
          msg.body.id = Math.random();
          this.ongoing[msg.body.id] = task;
      }

      peer.channel.postDH(peer.pubKey, msg, function() {});
  };

  /**
   * Acknowledge receiving a spend request
   * @private
   */
  DeliveryService.prototype.sendAck = function(peer, body) {
      // Add the task
      var msg = Protocol.AckMsg(body.id);
      peer.channel.postDH(peer.pubKey, msg, function() {});
  };

  /**
   * A new multisig ack has arrived
   */
  DeliveryService.prototype.onAck = function(msg) {
      var peer = msg.peer;
      var tracking = this.ongoing[msg.body.id];
      if (tracking) {
          tracking.ack = Date.now();
          delete this.ongoing[msg.body.id];
      }
  };

  /**
   * Send test message
   */
  DeliveryService.prototype.sendDeliveryTest = function(contact) {
      var peer = contact.online;
      var msg = Protocol.packMessage('DeliveryTest', {'text': 'test'});
      this.sendMessage(contact, msg);
  };

  /**
   * Receive test message
   */
  DeliveryService.prototype.onDeliveryTest = function(msg) {
      var peer = msg.peer;
      this.sendAck(peer, msg.body);
  };

  return DeliveryService;

});
<|endoftext|>


<commit_before>import React, { PropTypes, Component } from 'react';
import shouldPureComponentUpdate from 'react-pure-render/function';

export default class TopLeftLink extends Component {
	static contextTypes = {
		router: PropTypes.object.isRequired
	};
	static propTypes = {
		text		  : PropTypes.string,
		icon		  : PropTypes.string,
		onClickHandler: PropTypes.func,
		wrapIcon      : PropTypes.func
	};

	shouldComponentUpdate = shouldPureComponentUpdate;

	render() {
		const {onClickHandler, text, icon, wrapIcon} = this.props;
		return (

				{icon ?
                    wrapIcon ?

						:

					:

						{text}

				}

		);
	}
}
<commit_msg>Fix warning<commit_after>import React, { PropTypes, Component } from 'react';
import shouldPureComponentUpdate from 'react-pure-render/function';

export default class TopLeftLink extends Component {
	static contextTypes = {
		router: PropTypes.object.isRequired
	};
	static propTypes = {
		text		  : PropTypes.string,
		icon		  : PropTypes.string,
		onClickHandler: PropTypes.func,
		wrapIcon      : PropTypes.bool
	};

	shouldComponentUpdate = shouldPureComponentUpdate;

	render() {
		const {onClickHandler, text, icon, wrapIcon} = this.props;
		return (

				{icon ?
                    wrapIcon ?

						:

					:

						{text}

				}

		);
	}
}
<|endoftext|>


<commit_before>import { doms } from '../base';
import '../../css/details_box.css';

export const renderDetailsPopup = el => {
    const content = `

            Name:
            ${el.TECH}

            Category:
            ${el.CAT}

            Sub-category:
            ${el.SUBCAT}

            Date Added:
            ${el.DATE}

            Status:
            ${el.STATUS}

            Detail:
            ${el.DETAILS}

`;

    doms.detailsBox.innerHTML = content;
};

<commit_msg>Update detailsPopupView.js<commit_after>import { doms } from '../base';
import { fadeIn } from '../transitions';
import '../../css/details_box.css';

export const renderDetailsPopup = el => {
    const content = `

            Name:
            ${el.TECH}

            Category:
            ${el.CAT}

            Sub-category:
            ${el.SUBCAT}

            Date Added:
            ${el.DATE}

            Status:
            ${el.STATUS}

            Detail:
            ${el.DETAILS}

`;

    doms.detailsBox.innerHTML = content;
    fadeIn(doms.detailsBox);
};

<|endoftext|>


<commit_before>/**
 * Author: DrowsyFlesh
 * Create: 2019/2/16
 * Description:
 */

import {UI} from 'Libs/UI';
import React from 'react';
import ReactDOM from 'react-dom';
import {createGlobalStyle} from 'styled-components';

const DarkModeStyle = createGlobalStyle`
  html {
    --dark-1: #0c0c0c;
    --dark-2: #131313;
    --dark-3: #1f1f1f;
    --dark-4: #2b2b2b;

    --dark-font-0: #e8e8e8;
    --dark-font-1: #99a2aa;
    --dark-font-2: #879199;
  }

  body {
    background-color: #1c2022!important;
  }
  // 视频信息
  #v_desc {
    .info {
      color: #879199;
    }
  }
  // up信息
  .up-info .btn .b-cd {
    background: var(--dark-3)!important;
  }
  .bili-header-m .nav-menu .nav-mask {
    background-color: var(--dark-1)!important;
  }
  .nav-wrapper .nav-con ul li a {
    color: var(--dark-font-1)!important;
  }
  #viewbox_report .video-title .tit, .media-wrapper > h1, .media-right a, .media-right span {
    color: var(--dark-font-0)!important;
  }
  #bilibiliPlayer {
    box-shadow: 0 0 8px var(--dark-1)!important;
  }
  #paybar_module {
    .vip a.btn-pay.active {
      background-color: var(--dark-1)!important;
      border: 1px solid var(--dark-3);
    }
  }
  // tags
  #v_tag {
    border-color: var(--dark-1)!important;
    ul li {
      background-color: var(--dark-1)!important;
      border-color: var(--dark-1)!important;
      .tag-info-pane, a, .text {
        background-color: var(--dark-1)!important;
        color: var(--dark-font-1)!important;
     }
    }
    .btn-add, .ipt {
      background-color: var(--dark-2)!important;
      border-color: var(--dark-1)!important;
      color: var(--dark-font-1)!important;
    }
  }
  // 弹幕列表
  .player-auxiliary-area .player-auxiliary-danmaku .player-auxiliary-danmaku-function [class*=player-auxiliary-danmaku-btn-],
  .player-auxiliary-area .player-auxiliary-danmaku .player-auxiliary-danmaku-function,
  .danmaku-wrap .player-auxiliary-area .player-auxiliary-danmaku-wrap,
  .danmaku-wrap .player-auxiliary-area .player-auxiliary-danmaku-btn-history,
  .player-auxiliary-area {
    background: #191919!important;
  }
  .danmaku-wrap .player-auxiliary-area .player-auxiliary-danmaku-wrap .mCustomScrollBox {
    .mCSB_container .player-auxiliary-danmaku-list .danmaku-info-row {
      span {
        color: var(--dark-font-1)!important;
      }
    }
  }
  .v-wrap .danmaku-wrap {
    background-color: var(--dark-2)!important;
  }
  .bui-collapse-wrap {
    .bui-collapse-header, .player-auxiliary-filter {
      background-color: var(--dark-2)!important;
    }
  }
  .bui-collapse .bui-collapse-header, .player-auxiliary-filter {
    background: var(--dark-2);
    span {
      color: var(--dark-font-1);
    }
  }

  // 选集列表
  #eplist_module, #multi_page {
    background-color: var(--dark-2);
    .list-title, .head-con {
      h4, h3 {
        color: var(--dark-font-1);
      }
    }
    .module-box  {
      li {
        &:not(.on):not(:hover) {
          border-color: var(--dark-3);
          background-color: #232323;
          a {
            color: var(--dark-font-1);
          }
        }
        &:not(.on):hover {
          background-color: #232323;
        }
      }
    }
    .list-wrapper, .cur-list .list-box {
      &.simple {
        .ep-item:not(.cursor) {
          color: var(--dark-font-1);
          border-color: var(--dark-3);
          background-color: #232323;
        }
      }
      .ep-item, li {
        color: var(--dark-font-1);
        a {
          color: var(--dark-font-1);
        }
        &.cursor, & {
          background-color: #232323;
        }
        &.visited, &.watched {
          color: #757575;
        }
        &:hover {
          background-color: var(--dark-3);
          color: var(--dark-font-1);
        }
      }
    }
  }
  // 播放器工具栏
  .bilibili-player-video-sendbar {
    background-color: var(--dark-2)!important;
    .bilibili-player-video-inputbar {
      background-color: unset!important;
      .bilibili-player-video-inputbar-wrap {
        border-color: var(--dark-4)!important;
        background-color: var(--dark-4);
      }
      .bilibili-player-video-danmaku-input {
        color: var(--dark-font-1)!important;
      }
    }
  }

  // 交互栏
  #arc_toolbar_report, #toolbar_module, #media_module, .main-container .review-module {
    border-color: var(--dark-1)!important;
    .more-ops-list {
      background-color: var(--dark-1)!important;
      border-color: var(--dark-1)!important;
      box-shadow: unset!important;
      color: var(--dark-font-1)!important;
      li:hover {
        background-color: var(--dark-3)!important;
      }
    }
  }
  #media_module {
    .media-tool-bar {
      .btn-rating {
        background-color: var(--dark-1);
        border-color: var(--dark-1);
      }
    }
  }

  // 活动
  #activity_vote .inside-wrp {
    border-color: var(--dark-1);
        background-color: var(--dark-1);
    .left {
      color: var(--dark-font-1);
    }
  }

  // 评价
  #review_module {
    border-color: var(--dark-1)!important;
    .review-list .review-item .review-body {
      background-color: var(--dark-1)!important;
     .review-header .review-author, .review-title {
        color: var(--dark-font-1)
     }
     .review-content {
       color: var(--dark-font-1)!important;
     }
    }
  }

  // 承包榜
  #sponsor_module {
    border-color: var(--dark-1)!important;
    .sponsor-rank-header {
      h3, .sponsor-rank-tab li {
        color: var(--dark-font-1)!important;
      }
    }
    .sponsor-rank-item {
      .sp-right .sp-msg {
        color: var(--dark-font-1)!important;
        border-color: var(--dark-1)!important;
      }
    }
  }

  // 评论区
  #comment .b-head span{
    color: var(--dark-font-1);
  }
  #comment_module {
    border-color: var(--dark-1)!important;
    .b-head span{
      color: var(--dark-font-1);
    }
    .textarea-container textarea, .textarea-container:hover textarea {
      background-color: var(--dark-1);
      border: 1px solid var(--dark-1);
      color: var(--dark-font-1);
    }
    .comment-emoji {
      border: 1px solid var(--dark-1);
      background-color: var(--dark-1);
    }
    .emoji-box {
      background: var(--dark-1);
      border: 1px solid var(--dark-1);
      .emoji-wrap {

      }
    }
  }
  .comment-header {
    border-color: var(--dark-1)!important;
    .tabs-order li:not(.on):not(:hover) {
      color: var(--dark-font-1)!important;
    }
  }
  .opera-list {
    background-color: var(--dark-1)!important;
    border-color: var(--dark-1)!important;
    box-shadow: unset!important;
    color: var(--dark-font-1)!important;
    li:hover {
      background-color: var(--dark-3)!important;
    }
  }
  .bb-comment {
    background-color: unset!important;
    .header-page .result, .header-page a.tcd-number, .header-page span.dian {
      color: var(--dark-font-1);
    }
    .comment-list {
      .hot-line {
        border-color: var(--dark-1);
        span {
          background: var(--dark-1);
          color: var(--dark-font-1);
          a:hover {
            background: var(--dark-3);
          }
        }
      }
      .list-item .con {
        border-color: var(--dark-1)!important;
        .text {
          color: var(--dark-font-1);
        }
        .info .reply:hover {
          background: var(--dark-1);
        }
        .reply-box{
          .reply-item .reply-con {
            .user .text-con {
              color: var(--dark-font-1);
            }
            .info .reply:hover {
              background: var(--dark-1);
            }
          }
          .view-more .btn-more:hover {
            background: var(--dark-1);
          }
        }
      }
    }
  }
  // 右侧视频推荐区
  #recom_module {
    .recom-title, .recom-item .info-wrapper a{
      color: var(--dark-font-0);
      font-weight: 400;
    }
    .expand-more {
      background-color: var(--dark-1);
      color: var(--dark-font-1);
    }
  }
  #reco_list {
    .rec-title,
    .rec-list .video-page-card .card-box .info .title {
      color: var(--dark-font-1);
    }
    .rec-footer {
      background-color: var(--dark-1);
    }
  }
  // 番剧剧集信息
  #seasonlist_module {
    .series-title {
      color: var(--dark-font-1);
    }
    .ss-list-wrapper .ss-item .ss-info .ss-title {
      color: var(--dark-font-1);
    }
    .expand-more {
      background-color: var(--dark-1);
      color: var(--dark-font-1);
    }
  }
  // up信息
  #v_upinfo .u-info .name .username{
    color: var(--dark-font-1);
  }
  // 直播推荐
  #live_recommand_report .pl__card {
    border-color: var(--dark-1);
    background-color: var(--dark-1);
    .pl__info .pl__title {
      color: var(--dark-font-1);
    }
  }
`;

export class DarkModeUI extends UI {
    constructor() {
        super({
            name: 'darkMode',
        });
    }

    load = (containers, {on}) => {
        return new Promise((resolve) => {
            const wrapper = document.createElement('div');
            wrapper.setAttribute('class', 'bilibili-helper-dark-mode');
            wrapper.setAttribute('style', 'margin: 0;');
            document.querySelector('body').append(wrapper);
            ReactDOM.render(<React.Fragment>
                {on && <DarkModeStyle/>}
            </React.Fragment>, wrapper, () => resolve(wrapper));
        });
    };
}
<commit_msg>updage dark mode style<commit_after>/**
 * Author: DrowsyFlesh
 * Create: 2019/2/16
 * Description:
 */

import {UI} from 'Libs/UI';
import React from 'react';
import ReactDOM from 'react-dom';
import {createGlobalStyle} from 'styled-components';

const DarkModeStyle = createGlobalStyle`
  html {
    --dark-1: #0c0c0c;
    --dark-2: #131313;
    --dark-3: #1f1f1f;
    --dark-4: #2b2b2b;

    --dark-font-0: #e8e8e8;
    --dark-font-1: #99a2aa;
    --dark-font-2: #879199;
  }

  body {
    background-color: #1c2022!important;
  }
  // 视频信息
  #v_desc {
    .info {
      color: #879199;
    }
  }
  // up信息
  .up-info .btn .b-cd {
    background: var(--dark-3)!important;
  }
  .bili-header-m .nav-menu .nav-mask {
    background-color: var(--dark-1)!important;
  }
  .nav-wrapper .nav-con ul li a {
    color: var(--dark-font-1)!important;
  }
  #viewbox_report .video-title .tit, .media-wrapper > h1, .media-right a, .media-right span {
    color: var(--dark-font-0)!important;
  }
  #bilibiliPlayer {
    box-shadow: 0 0 8px var(--dark-1)!important;
  }
  #paybar_module {
    .vip a.btn-pay.active {
      background-color: var(--dark-1)!important;
      border: 1px solid var(--dark-3);
    }
  }
  // tags
  #v_tag {
    border-color: var(--dark-1)!important;
    ul li {
      background-color: var(--dark-1)!important;
      border-color: var(--dark-1)!important;
      .tag-info-pane, a, .text {
        background-color: var(--dark-1)!important;
        color: var(--dark-font-1)!important;
     }
    }
    .btn-add, .ipt {
      background-color: var(--dark-2)!important;
      border-color: var(--dark-1)!important;
      color: var(--dark-font-1)!important;
    }
  }
  // 弹幕列表
  .player-auxiliary-area .player-auxiliary-danmaku .player-auxiliary-danmaku-function [class*=player-auxiliary-danmaku-btn-],
  .player-auxiliary-area .player-auxiliary-danmaku .player-auxiliary-danmaku-function,
  .danmaku-wrap .player-auxiliary-area .player-auxiliary-danmaku-wrap,
  .danmaku-wrap .player-auxiliary-area .player-auxiliary-danmaku-btn-history,
  .player-auxiliary-area {
    background: #191919!important;
  }
  .danmaku-wrap .player-auxiliary-area .player-auxiliary-danmaku-wrap .mCustomScrollBox {
    .mCSB_container .player-auxiliary-danmaku-list .danmaku-info-row {
      span {
        color: var(--dark-font-1)!important;
      }
    }
  }
  .v-wrap .danmaku-wrap {
    background-color: var(--dark-2)!important;
  }
  .bui-collapse-wrap {
    .bui-collapse-header, .player-auxiliary-filter {
      background-color: var(--dark-2)!important;
    }
  }
  .bui-collapse .bui-collapse-header, .player-auxiliary-filter {
    background: var(--dark-2);
    span {
      color: var(--dark-font-1);
    }
  }

  // 选集列表
  #eplist_module, #multi_page {
    background-color: var(--dark-2);
    .list-title, .head-con {
      h4, h3 {
        color: var(--dark-font-1);
      }
    }
    .module-box  {
      li {
        &:not(.on):not(:hover) {
          border-color: var(--dark-3);
          background-color: #232323;
          a {
            color: var(--dark-font-1);
          }
        }
        &:not(.on):hover {
          background-color: #232323;
        }
      }
    }
    .list-wrapper, .cur-list .list-box {
      &.simple {
        .ep-item:not(.cursor) {
          color: var(--dark-font-1);
          border-color: var(--dark-3);
          background-color: #232323;
        }
      }
      .ep-item, li {
        color: var(--dark-font-1);
        a {
          color: var(--dark-font-1);
        }
        &.cursor, & {
          background-color: #232323;
        }
        &.visited, &.watched {
          color: #757575;
        }
        &:hover {
          background-color: var(--dark-3);
          color: var(--dark-font-1);
        }
      }
    }
  }
  // 播放器工具栏
  .bilibili-player-video-sendbar {
    background-color: var(--dark-2)!important;
    .bilibili-player-video-inputbar {
      background-color: unset!important;
      .bilibili-player-video-inputbar-wrap {
        border-color: var(--dark-4)!important;
        background-color: var(--dark-4);
      }
      .bilibili-player-video-danmaku-input {
        color: var(--dark-font-1)!important;
      }
    }
  }

  // 交互栏
  #arc_toolbar_report, #toolbar_module, #media_module, .main-container .review-module {
    border-color: var(--dark-1)!important;
    .more-ops-list {
      background-color: var(--dark-1)!important;
      border-color: var(--dark-1)!important;
      box-shadow: unset!important;
      color: var(--dark-font-1)!important;
      li:hover {
        background-color: var(--dark-3)!important;
      }
    }
  }
  #media_module {
    .media-tool-bar {
      .btn-rating {
        background-color: var(--dark-1);
        border-color: var(--dark-1);
      }
    }
  }

  // 活动
  #activity_vote .inside-wrp {
    border-color: var(--dark-1);
        background-color: var(--dark-1);
    .left {
      color: var(--dark-font-1);
    }
  }

  // 评价
  #review_module {
    border-color: var(--dark-1)!important;
    .review-list .review-item .review-body {
      background-color: var(--dark-1)!important;
     .review-header .review-author, .review-title {
        color: var(--dark-font-1)
     }
     .review-content {
       color: var(--dark-font-1)!important;
     }
    }
  }

  // 承包榜
  #sponsor_module {
    border-color: var(--dark-1)!important;
    .sponsor-rank-header {
      h3, .sponsor-rank-tab li {
        color: var(--dark-font-1)!important;
      }
    }
    .sponsor-rank-item {
      .sp-right .sp-msg {
        color: var(--dark-font-1)!important;
        border-color: var(--dark-1)!important;
        background-color: var(--dark-1)!important;
      }
    }
  }

  // 评论区
  #comment .b-head span{
    color: var(--dark-font-1);
  }
  #comment_module, #comment {
    border-color: var(--dark-1)!important;
    .b-head span{
      color: var(--dark-font-1);
    }
    .textarea-container textarea, .textarea-container:hover textarea {
      background-color: var(--dark-1);
      border: 1px solid var(--dark-1);
      color: var(--dark-font-1);
    }
    .comment-emoji {
      border: 1px solid var(--dark-1);
      background-color: var(--dark-1);
    }
    .emoji-box {
      background: var(--dark-1);
      border: 1px solid var(--dark-1);
      .emoji-wrap {

      }
    }
  }
  .comment-header {
    border-color: var(--dark-1)!important;
    .tabs-order li:not(.on):not(:hover) {
      color: var(--dark-font-1)!important;
    }
  }
  .opera-list {
    background-color: var(--dark-1)!important;
    border-color: var(--dark-1)!important;
    box-shadow: unset!important;
    color: var(--dark-font-1)!important;
    li:hover {
      background-color: var(--dark-3)!important;
    }
  }
  .bb-comment {
    background-color: unset!important;
    .header-page .result, .header-page a.tcd-number, .header-page span.dian {
      color: var(--dark-font-1);
    }
    .comment-list {
      .hot-line {
        border-color: var(--dark-1);
        span {
          background: var(--dark-1);
          color: var(--dark-font-1);
          a:hover {
            background: var(--dark-3);
          }
        }
      }
      .list-item .con {
        border-color: var(--dark-1)!important;
        .text {
          color: var(--dark-font-1);
        }
        .info .reply:hover {
          background: var(--dark-1);
        }
        .reply-box{
          .reply-item .reply-con {
            .user .text-con {
              color: var(--dark-font-1);
            }
            .info .reply:hover {
              background: var(--dark-1);
            }
          }
          .view-more .btn-more:hover {
            background: var(--dark-1);
          }
        }
      }
    }
  }
  // 右侧视频推荐区
  #recom_module {
    .recom-title, .recom-item .info-wrapper a{
      color: var(--dark-font-0);
      font-weight: 400;
    }
    .expand-more {
      background-color: var(--dark-1);
      color: var(--dark-font-1);
    }
  }
  #reco_list {
    .rec-title,
    .rec-list .video-page-card .card-box .info .title {
      color: var(--dark-font-1);
    }
    .rec-footer {
      background-color: var(--dark-1);
    }
  }
  // 番剧剧集信息
  #seasonlist_module {
    .series-title {
      color: var(--dark-font-1);
    }
    .ss-list-wrapper .ss-item .ss-info .ss-title {
      color: var(--dark-font-1);
    }
    .expand-more {
      background-color: var(--dark-1);
      color: var(--dark-font-1);
    }
  }
  // up信息
  #v_upinfo .u-info .name .username{
    color: var(--dark-font-1);
  }
  // 直播推荐
  #live_recommand_report .pl__card {
    border-color: var(--dark-1);
    background-color: var(--dark-1);
    .pl__info .pl__title {
      color: var(--dark-font-1);
    }
  }
`;

export class DarkModeUI extends UI {
    constructor() {
        super({
            name: 'darkMode',
        });
    }

    load = (containers, {on}) => {
        return new Promise((resolve) => {
            const wrapper = document.createElement('div');
            wrapper.setAttribute('class', 'bilibili-helper-dark-mode');
            wrapper.setAttribute('style', 'margin: 0;');
            document.querySelector('body').append(wrapper);
            ReactDOM.render(<React.Fragment>
                {on && <DarkModeStyle/>}
            </React.Fragment>, wrapper, () => resolve(wrapper));
        });
    };
}
<|endoftext|>