<commit_before>import PropTypes from 'prop-types'

import {
  DIALOG_MEDIA_VIEWER,
} from 'src/constants'
import TimelineData, {postStatusManaged} from 'src/infra/TimelineData'
import PushDialogUseCase from 'src/usecases/PushDialogUseCase'

export const TimelineActionPropTypes = {
  // Accountがなんか押された
  onAvatarClicked: PropTypes.func.isRequired,
  // Reply送信ボタンが押された
  onSendReply: PropTypes.func.isRequired,
  // Fav/UnfavがClickされた
  onFavouriteStatus: PropTypes.func.isRequired,
  // Reblog/UnreblogがClickされた
  onReblogStatus: PropTypes.func.isRequired,
}

export default class TimelineActions {
  constructor(context) {
    this.context = context
  }

  get props() {
    return {
      onAvatarClicked: ::this.onAvatarClicked,
      onSendReply: ::this.onSendReply,
      onReblogStatus: ::this.onReblogStatus,
      onFavouriteStatus: ::this.onFavouriteStatus,
      onClickMedia: ::this.onClickMedia,
    }
  }

  onAvatarClicked(account) {
    // TODO: named routingしたい
    const {app} = this.context
    app.pushState({}, null, `/user/@${account.acct}`)
  }

  async onSendReply(status, sendFrom, messageContent) {
    // とりまこっから送る
    await Promise.all(
      sendFrom.map(async (token) => {
        // in_reply_to_id を付加する
        messageContent.message.in_reply_to_id = status.getInReplyToIdByHost(token.host)
        // TODO: tootpanelの方にwarning出す?
        return await postStatusManaged(token, messageContent)
      })
    )
  }

  async onReblogStatus(token, status, toReblog) {
    const api = toReblog ? 'reblogStatus' : 'unreblogStatus'
    const {entities, result} = await token.requester[api]({
      id: status.getIdByHost(token.host),
    }, {token})
    return TimelineData.mergeStatuses(entities, [result])[0]
  }

  async onFavouriteStatus(token, status, toFav) {
    const api = toFav ? 'favouriteStatus' : 'unfavouriteStatus'
    const {entities, result} = await token.requester[api]({
      id: status.getIdByHost(token.host),
    }, {token})
    return TimelineData.mergeStatuses(entities, [result])[0]
  }

  onClickMedia(media) {
    const {context} = this.context

    context.useCase(
      new PushDialogUseCase()
    ).execute(DIALOG_MEDIA_VIEWER, {media})
  }
}
<commit_msg>返信時にin_reply_to_idが付いてなかった<commit_after>import PropTypes from 'prop-types'

import {
  DIALOG_MEDIA_VIEWER,
} from 'src/constants'
import TimelineData, {postStatusManaged} from 'src/infra/TimelineData'
import PushDialogUseCase from 'src/usecases/PushDialogUseCase'

export const TimelineActionPropTypes = {
  // Accountがなんか押された
  onAvatarClicked: PropTypes.func.isRequired,
  // Reply送信ボタンが押された
  onSendReply: PropTypes.func.isRequired,
  // Fav/UnfavがClickされた
  onFavouriteStatus: PropTypes.func.isRequired,
  // Reblog/UnreblogがClickされた
  onReblogStatus: PropTypes.func.isRequired,
}

export default class TimelineActions {
  constructor(context) {
    this.context = context
  }

  get props() {
    return {
      onAvatarClicked: ::this.onAvatarClicked,
      onSendReply: ::this.onSendReply,
      onReblogStatus: ::this.onReblogStatus,
      onFavouriteStatus: ::this.onFavouriteStatus,
      onClickMedia: ::this.onClickMedia,
    }
  }

  onAvatarClicked(account) {
    // TODO: named routingしたい
    const {app} = this.context
    app.pushState({}, null, `/user/@${account.acct}`)
  }

  async onSendReply(status, sendFrom, messageContent) {
    // とりまこっから送る
    await Promise.all(
      sendFrom.map(async (token) => {
        // in_reply_to_id を付加する
        messageContent.message.in_reply_to_id = status.getIdByHost(token.host)
        // TODO: tootpanelの方にwarning出す?
        return await postStatusManaged(token, messageContent)
      })
    )
  }

  async onReblogStatus(token, status, toReblog) {
    const api = toReblog ? 'reblogStatus' : 'unreblogStatus'
    const {entities, result} = await token.requester[api]({
      id: status.getIdByHost(token.host),
    }, {token})
    return TimelineData.mergeStatuses(entities, [result])[0]
  }

  async onFavouriteStatus(token, status, toFav) {
    const api = toFav ? 'favouriteStatus' : 'unfavouriteStatus'
    const {entities, result} = await token.requester[api]({
      id: status.getIdByHost(token.host),
    }, {token})
    return TimelineData.mergeStatuses(entities, [result])[0]
  }

  onClickMedia(media) {
    const {context} = this.context

    context.useCase(
      new PushDialogUseCase()
    ).execute(DIALOG_MEDIA_VIEWER, {media})
  }
}
<|endoftext|>


<commit_before>angular
  .module("animeHub")
  .controller("animesController", animesController);

animesController.$inject =['$stateParams', 'Anime', 'Comment', 'TokenService'];
function animesController($stateParams, Anime, Comment, TokenService){
  // object saved as self
  var self = this;

  // decoded info of user
  self.userToken = TokenService.getUser();
  // model where comment form data are saved
  self.commentModel = {};

  // ---- ANIME -----

  // gat all the anime
  Anime.query(function(res) {
    self.all = res.animes;
  });

  // get one anime
  function getOne() {
    Anime.get({ id: $stateParams.animeId }, function(res) {
      console.log(res.anime); // console log one anime object
      self.selectedAnime = res.anime;
    });
  }

  // if there is params get one anime
  if ($stateParams.animeId) {
    getOne();
  }

  // ---- COMMENTS -----

  self.createComment = function(animeId) {
    self.commentModel.user = self.userToken._id;
    console.log(self.commentModel);
    Comment.save(
      { animeId: animeId }, self.commentModel,
      // success
      function(res) {
        self.commentModel = {};
      },
      // error
      function(err) {
        console.log(err.data.message);
      }
    );
  };

  self.removeComment = function(id) {
    Comment.remove({id:id});
  };

}<commit_msg>can add to array new comment with static values<commit_after>angular
  .module("animeHub")
  .controller("animesController", animesController);

animesController.$inject =['$stateParams', 'Anime', 'Comment', 'TokenService'];
function animesController($stateParams, Anime, Comment, TokenService){
  // object saved as self
  var self = this;

  // decoded info of user
  self.userToken = TokenService.getUser();
  // model where comment form data are saved
  self.commentModel = {};

  // ---- ANIME -----

  // gat all the anime
  Anime.query(function(res) {
    self.all = res.animes;
  });

  // get one anime
  function getOne() {
    Anime.get({ id: $stateParams.animeId }, function(res) {
      console.log('Anime:', res.anime); // console log one anime object
      self.selectedAnime = res.anime;
    });
  }

  // if there is params get one anime
  if ($stateParams.animeId) {
    getOne();
  }

  // ---- COMMENTS -----

  self.createComment = function(animeId) {
    self.commentModel.user = self.userToken._id;
    console.log(self.commentModel);
    Comment.save(
      { animeId: animeId }, self.commentModel,
      // success
      function(res) {
        self.commentModel = {};
        var newComment = {
          title: "stuf",
          content: "boop"
        }
        console.log('Comments Array:', self.selectedAnime.comments)
        self.selectedAnime.comments.push(newComment)
        console.log('Comments Array:', self.selectedAnime.comments)
      },
      // error
      function(err) {
        console.log(err.data.message);
      }
    );
  };

  self.removeComment = function(id) {
    Comment.remove({id:id});
  };

}<|endoftext|>


<commit_before>/*
 * L.TimeDimension.Layer.WMS: wms Layer associated to a TimeDimension
 */

L.TimeDimension.Layer.WMS = L.TimeDimension.Layer.extend({

    initialize: function(layer, options) {
        L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
        this._timeCacheBackward = this.options.cacheBackward || this.options.cache || 0;
        this._timeCacheForward = this.options.cacheForward || this.options.cache || 0;
        this._wmsVersion = this.options.wmsVersion || this.options.version || layer.options.version || "1.1.1";
        this._getCapabilitiesParams = this.options.getCapabilitiesParams || {};
        this._getCapabilitiesAlternateUrl = this.options.getCapabilitiesUrl || null;
        this._getCapabilitiesAlternateLayerName = this.options.getCapabilitiesLayerName || null;
        this._proxy = this.options.proxy || null;
        this._updateTimeDimension = this.options.updateTimeDimension || false;
        this._setDefaultTime = this.options.setDefaultTime || false;
        this._updateTimeDimensionMode = this.options.updateTimeDimensionMode || 'intersect'; // 'union' or 'replace'
        this._period = this.options.period || null;
        this._layers = {};
        this._defaultTime = 0;
        this._availableTimes = [];
        this._capabilitiesRequested = false;
        if (this._updateTimeDimension || this.options.requestTimeFromCapabilities) {
            this._requestTimeDimensionFromCapabilities();
        }

        this._baseLayer.on('load', (function() {
            this._baseLayer.setLoaded(true);
            this.fire('timeload', {
                time: this._defaultTime
            });
        }).bind(this));
    },

    getEvents: function() {
        var clearCache = L.bind(this._unvalidateCache, this);
        return {
            moveend: clearCache,
            zoomend: clearCache
        }
    },

    eachLayer: function(method, context) {
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                method.call(context, this._layers[prop]);
            }
        }
        return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context);
    },

    _onNewTimeLoading: function(ev) {
        // console.log('Layer._onNewTimeLoading: ' + this._baseLayer.wmsParams.layers + ' with time: ' + new Date(ev.time).toISOString());
        var layer = this._getLayerForTime(ev.time);
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
            // console.log('Layer._onNewTimeLoading: layer added to map');
        }
    },

    isReady: function(time) {
        var layer = this._getLayerForTime(time);
        if (this.options.bounds && this._map)
            if (!this._map.getBounds().contains(this.options.bounds))
                return true;
        return layer.isLoaded();
    },

    onAdd: function(map) {
        L.TimeDimension.Layer.prototype.onAdd.call(this, map);
        if (this._availableTimes.length == 0) {
            this._requestTimeDimensionFromCapabilities();
        } else {
            this._updateTimeDimensionAvailableTimes();
        }
    },

    _update: function() {
        if (!this._map)
            return;
        var time = this._timeDimension.getCurrentTime();
        // It will get the layer for this time (create or get)
        // Then, the layer will be loaded if necessary, adding it to the map (and show it after loading).
        // If it already on the map (but probably hidden), it will be shown
        var layer = this._getLayerForTime(time);
        if (this._currentLayer == null) {
            this._currentLayer = layer;
        }
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
        } else {
            this._showLayer(layer, time);
        }
    },

    setOpacity: function(opacity) {
        L.TimeDimension.Layer.prototype.setOpacity.apply(this, arguments);
        // apply to all preloaded caches
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setOpacity) {
                this._layers[prop].setOpacity(opacity);
            }
        }
    },

    setZIndex: function(zIndex){
        L.TimeDimension.Layer.prototype.setZIndex.apply(this, arguments);
        // apply to all preloaded caches
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setZIndex) {
                this._layers[prop].setZIndex(zIndex);
            }
        }
    },

    setParams: function(params, noRedraw) {
        L.extend(this._baseLayer.options, params);
        if (this._baseLayer.setParams) {
            this._baseLayer.setParams(params, noRedraw);
        }
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setParams) {
                this._layers[prop].setLoaded(false); // mark it as unloaded
                this._layers[prop].setParams(params, noRedraw);
            }
        }
        return this;
    },

    _unvalidateCache: function() {
        var time = this._timeDimension.getCurrentTime();
        for (var prop in this._layers) {
            if (time != prop && this._layers.hasOwnProperty(prop)) {
                this._layers[prop].setLoaded(false); // mark it as unloaded
                this._layers[prop].redraw();
            }
        }
    },

    _evictCachedTimes: function(keepforward, keepbackward) {
        // Cache management
        var times = this._getLoadedTimes();
        var strTime = String(this._currentTime);
        var index = times.indexOf(strTime);
        var remove = [];
        // remove times before current time
        if (keepbackward > -1) {
            var objectsToRemove = index - keepbackward;
            if (objectsToRemove > 0) {
                remove = times.splice(0, objectsToRemove);
                this._removeLayers(remove);
            }
        }
        if (keepforward > -1) {
            index = times.indexOf(strTime);
            var objectsToRemove = times.length - index - keepforward - 1;
            if (objectsToRemove > 0) {
                remove = times.splice(index + keepforward + 1, objectsToRemove);
                this._removeLayers(remove);
            }
        }
    },
    _showLayer: function(layer, time) {
        if (this._currentLayer && this._currentLayer !== layer) {
            this._currentLayer.hide();
        }
        layer.show();
        if (this._currentLayer && this._currentLayer === layer) {
            return;
        }
        this._currentLayer = layer;
        this._currentTime = time;
        console.log('Show layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());

        this._evictCachedTimes(this._timeCacheForward, this._timeCacheBackward);
    },

    _getLayerForTime: function(time) {
        if (time == 0 || time == this._defaultTime || time == null) {
            return this._baseLayer;
        }
        if (this._layers.hasOwnProperty(time)) {
            return this._layers[time];
        }
        var nearestTime = this._getNearestTime(time);
        if (this._layers.hasOwnProperty(nearestTime)) {
            return this._layers[nearestTime];
        }

        var newLayer = this._createLayerForTime(nearestTime);

        this._layers[time] = newLayer;

        newLayer.on('load', (function(layer, time) {
            layer.setLoaded(true);
            // this time entry should exists inside _layers
            // but it might be deleted by cache management
            if (!this._layers[time]) {
                this._layers[time] = layer;
            }
            if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
                this._showLayer(layer, time);
            }
            // console.log('Loaded layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());
            this.fire('timeload', {
                time: time
            });
        }).bind(this, newLayer, time));

        // Hack to hide the layer when added to the map.
        // It will be shown when timeload event is fired from the map (after all layers are loaded)
        newLayer.onAdd = (function(map) {
            Object.getPrototypeOf(this).onAdd.call(this, map);
            this.hide();
        }).bind(newLayer);
        return newLayer;
    },

    _createLayerForTime:function(time){
        var wmsParams = this._baseLayer.options;
        wmsParams.time = new Date(time).toISOString();
        return new this._baseLayer.constructor(this._baseLayer.getURL(), wmsParams);
    },

    _getLoadedTimes: function() {
        var result = [];
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                result.push(prop);
            }
        }
        return result.sort(function(a, b) {
            return a - b;
        });
    },

    _removeLayers: function(times) {
        for (var i = 0, l = times.length; i < l; i++) {
            if (this._map)
                this._map.removeLayer(this._layers[times[i]]);
            delete this._layers[times[i]];
        }
    },

    setMinimumForwardCache: function(value) {
        if (value > this._timeCacheForward) {
            this._timeCacheForward = value;
        }
    },

    _requestTimeDimensionFromCapabilities: function() {
        if (this._capabilitiesRequested) {
            return;
        }
        this._capabilitiesRequested = true;
        var url = this._getCapabilitiesUrl();
        if (this._proxy) {
            url = this._proxy + '?url=' + encodeURIComponent(url);
        }
        var oReq = new XMLHttpRequest();
        oReq.addEventListener("load", (function(xhr) {
            var data = xhr.currentTarget.responseXML;
            this._defaultTime = Date.parse(this._getDefaultTimeFromCapabilities(data));
            this._setDefaultTime = this._setDefaultTime || (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0);
            this.setAvailableTimes(this._parseTimeDimensionFromCapabilities(data));
            if (this._setDefaultTime && this._timeDimension) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }).bind(this));
        oReq.overrideMimeType('application/xml');
        oReq.open("GET", url);
        oReq.send();
    },

    _getCapabilitiesUrl: function() {
        var url = this._baseLayer.getURL();
        if (this._getCapabilitiesAlternateUrl)
            url = this._getCapabilitiesAlternateUrl;
        var params = L.extend({}, this._getCapabilitiesParams, {
          'request': 'GetCapabilities',
          'service': 'WMS',
          'version': this._wmsVersion
        });
        url = url + L.Util.getParamString(params, url, params.uppercase);
        return url;
    },

    _parseTimeDimensionFromCapabilities: function(xml) {
        var layers = xml.querySelectorAll('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layer = null;
        var times = null;

        layers.forEach(function(current) {
            if (current.querySelector("Name").innerHTML === layerName) {
                layer = current;
            }
        })
        if (layer) {
            times = this._getTimesFromLayerCapabilities(layer);
            if (!times) {
                times = this._getTimesFromLayerCapabilities(layer.parentNode);
            }
        }

        return times;
    },

    _getTimesFromLayerCapabilities: function(layer) {
        var times = Array.from(layer.children).filter(function(c) {
          return (c.nodeName === 'Extent' || c.nodeName === 'Dimension') &&
                  c.getAttribute('name') === 'time' &&
                  c.textContent.length;
        });
        return (times && times.length) ? times[0].textContent.trim() : 0;
    },

    _getDefaultTimeFromCapabilities: function(xml) {
        var layers = xml.querySelectorAll('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layer = null;
        var times = null;

        layers.forEach(function(current) {
            if (current.querySelector("Name").innerHTML === layerName) {
                layer = current;
            }
        })

        var defaultTime = 0;
        if (layer) {
            defaultTime = this._getDefaultTimeFromLayerCapabilities(layer);
            if (defaultTime == 0) {
                defaultTime = this._getDefaultTimeFromLayerCapabilities(layer.parentNode);
            }
        }
        return defaultTime;
    },

    _getDefaultTimeFromLayerCapabilities: function(layer) {
        var times = Array.from(layer.children).filter(function(c) {
          return (c.nodeName === 'Extent' || c.nodeName === 'Dimension') &&
                  c.getAttribute('name') === 'time' &&
                  c.textContent.length;
        });
        return (times && times.length) ? times[0].attributes.default : 0;
    },

    setAvailableTimes: function(times) {
        this._availableTimes = L.TimeDimension.Util.parseTimesExpression(times, this._period);
        this._updateTimeDimensionAvailableTimes();
    },

    _updateTimeDimensionAvailableTimes: function() {
        if ((this._timeDimension && this._updateTimeDimension) ||
            (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0)) {
            this._timeDimension.setAvailableTimes(this._availableTimes, this._updateTimeDimensionMode);
            if (this._setDefaultTime && this._defaultTime > 0) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }
    },

    _getNearestTime: function(time) {
        if (this._layers.hasOwnProperty(time)) {
            return time;
        }
        if (this._availableTimes.length == 0) {
            return time;
        }
        var index = 0;
        var len = this._availableTimes.length;
        for (; index < len; index++) {
            if (time < this._availableTimes[index]) {
                break;
            }
        }
        // We've found the first index greater than the time. Get the previous
        if (index > 0) {
            index--;
        }
        if (time != this._availableTimes[index]) {
            console.log('Search layer time: ' + new Date(time).toISOString());
            console.log('Return layer time: ' + new Date(this._availableTimes[index]).toISOString());
        }
        return this._availableTimes[index];
    },

});

if (!L.NonTiledLayer) {
    L.NonTiledLayer = (L.Layer || L.Class).extend({});
}

L.NonTiledLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.NonTiledLayer.prototype._update,
    _originalOnRemove: L.NonTiledLayer.prototype.onRemove,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    onRemove: function(map) {
        this._loaded = false;
        this._originalOnRemove(map);
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        this._div.style.display = 'none';
    },

    show: function() {
        this._visible = true;
        this._div.style.display = 'block';
    },

    getURL: function() {
        return this._wmsUrl;
    }

});

L.TileLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.TileLayer.prototype._update,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        if (this._container) {
            this._container.style.display = 'none';
        }
    },

    show: function() {
        this._visible = true;
        if (this._container) {
            this._container.style.display = 'block';
        }
    },

    getURL: function() {
        return this._url;
    }

});

L.timeDimension.layer.wms = function(layer, options) {
    return new L.TimeDimension.Layer.WMS(layer, options);
};
<commit_msg>Replace Array.from in PR #148 and fix getDefaultTimeFromLayerCapabilities<commit_after>/*
 * L.TimeDimension.Layer.WMS: wms Layer associated to a TimeDimension
 */

L.TimeDimension.Layer.WMS = L.TimeDimension.Layer.extend({

    initialize: function(layer, options) {
        L.TimeDimension.Layer.prototype.initialize.call(this, layer, options);
        this._timeCacheBackward = this.options.cacheBackward || this.options.cache || 0;
        this._timeCacheForward = this.options.cacheForward || this.options.cache || 0;
        this._wmsVersion = this.options.wmsVersion || this.options.version || layer.options.version || "1.1.1";
        this._getCapabilitiesParams = this.options.getCapabilitiesParams || {};
        this._getCapabilitiesAlternateUrl = this.options.getCapabilitiesUrl || null;
        this._getCapabilitiesAlternateLayerName = this.options.getCapabilitiesLayerName || null;
        this._proxy = this.options.proxy || null;
        this._updateTimeDimension = this.options.updateTimeDimension || false;
        this._setDefaultTime = this.options.setDefaultTime || false;
        this._updateTimeDimensionMode = this.options.updateTimeDimensionMode || 'intersect'; // 'union' or 'replace'
        this._period = this.options.period || null;
        this._layers = {};
        this._defaultTime = 0;
        this._availableTimes = [];
        this._capabilitiesRequested = false;
        if (this._updateTimeDimension || this.options.requestTimeFromCapabilities) {
            this._requestTimeDimensionFromCapabilities();
        }

        this._baseLayer.on('load', (function() {
            this._baseLayer.setLoaded(true);
            this.fire('timeload', {
                time: this._defaultTime
            });
        }).bind(this));
    },

    getEvents: function() {
        var clearCache = L.bind(this._unvalidateCache, this);
        return {
            moveend: clearCache,
            zoomend: clearCache
        }
    },

    eachLayer: function(method, context) {
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                method.call(context, this._layers[prop]);
            }
        }
        return L.TimeDimension.Layer.prototype.eachLayer.call(this, method, context);
    },

    _onNewTimeLoading: function(ev) {
        // console.log('Layer._onNewTimeLoading: ' + this._baseLayer.wmsParams.layers + ' with time: ' + new Date(ev.time).toISOString());
        var layer = this._getLayerForTime(ev.time);
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
            // console.log('Layer._onNewTimeLoading: layer added to map');
        }
    },

    isReady: function(time) {
        var layer = this._getLayerForTime(time);
        if (this.options.bounds && this._map)
            if (!this._map.getBounds().contains(this.options.bounds))
                return true;
        return layer.isLoaded();
    },

    onAdd: function(map) {
        L.TimeDimension.Layer.prototype.onAdd.call(this, map);
        if (this._availableTimes.length == 0) {
            this._requestTimeDimensionFromCapabilities();
        } else {
            this._updateTimeDimensionAvailableTimes();
        }
    },

    _update: function() {
        if (!this._map)
            return;
        var time = this._timeDimension.getCurrentTime();
        // It will get the layer for this time (create or get)
        // Then, the layer will be loaded if necessary, adding it to the map (and show it after loading).
        // If it already on the map (but probably hidden), it will be shown
        var layer = this._getLayerForTime(time);
        if (this._currentLayer == null) {
            this._currentLayer = layer;
        }
        if (!this._map.hasLayer(layer)) {
            this._map.addLayer(layer);
        } else {
            this._showLayer(layer, time);
        }
    },

    setOpacity: function(opacity) {
        L.TimeDimension.Layer.prototype.setOpacity.apply(this, arguments);
        // apply to all preloaded caches
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setOpacity) {
                this._layers[prop].setOpacity(opacity);
            }
        }
    },

    setZIndex: function(zIndex){
        L.TimeDimension.Layer.prototype.setZIndex.apply(this, arguments);
        // apply to all preloaded caches
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setZIndex) {
                this._layers[prop].setZIndex(zIndex);
            }
        }
    },

    setParams: function(params, noRedraw) {
        L.extend(this._baseLayer.options, params);
        if (this._baseLayer.setParams) {
            this._baseLayer.setParams(params, noRedraw);
        }
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop) && this._layers[prop].setParams) {
                this._layers[prop].setLoaded(false); // mark it as unloaded
                this._layers[prop].setParams(params, noRedraw);
            }
        }
        return this;
    },

    _unvalidateCache: function() {
        var time = this._timeDimension.getCurrentTime();
        for (var prop in this._layers) {
            if (time != prop && this._layers.hasOwnProperty(prop)) {
                this._layers[prop].setLoaded(false); // mark it as unloaded
                this._layers[prop].redraw();
            }
        }
    },

    _evictCachedTimes: function(keepforward, keepbackward) {
        // Cache management
        var times = this._getLoadedTimes();
        var strTime = String(this._currentTime);
        var index = times.indexOf(strTime);
        var remove = [];
        // remove times before current time
        if (keepbackward > -1) {
            var objectsToRemove = index - keepbackward;
            if (objectsToRemove > 0) {
                remove = times.splice(0, objectsToRemove);
                this._removeLayers(remove);
            }
        }
        if (keepforward > -1) {
            index = times.indexOf(strTime);
            var objectsToRemove = times.length - index - keepforward - 1;
            if (objectsToRemove > 0) {
                remove = times.splice(index + keepforward + 1, objectsToRemove);
                this._removeLayers(remove);
            }
        }
    },
    _showLayer: function(layer, time) {
        if (this._currentLayer && this._currentLayer !== layer) {
            this._currentLayer.hide();
        }
        layer.show();
        if (this._currentLayer && this._currentLayer === layer) {
            return;
        }
        this._currentLayer = layer;
        this._currentTime = time;
        console.log('Show layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());

        this._evictCachedTimes(this._timeCacheForward, this._timeCacheBackward);
    },

    _getLayerForTime: function(time) {
        if (time == 0 || time == this._defaultTime || time == null) {
            return this._baseLayer;
        }
        if (this._layers.hasOwnProperty(time)) {
            return this._layers[time];
        }
        var nearestTime = this._getNearestTime(time);
        if (this._layers.hasOwnProperty(nearestTime)) {
            return this._layers[nearestTime];
        }

        var newLayer = this._createLayerForTime(nearestTime);

        this._layers[time] = newLayer;

        newLayer.on('load', (function(layer, time) {
            layer.setLoaded(true);
            // this time entry should exists inside _layers
            // but it might be deleted by cache management
            if (!this._layers[time]) {
                this._layers[time] = layer;
            }
            if (this._timeDimension && time == this._timeDimension.getCurrentTime() && !this._timeDimension.isLoading()) {
                this._showLayer(layer, time);
            }
            // console.log('Loaded layer ' + layer.wmsParams.layers + ' with time: ' + new Date(time).toISOString());
            this.fire('timeload', {
                time: time
            });
        }).bind(this, newLayer, time));

        // Hack to hide the layer when added to the map.
        // It will be shown when timeload event is fired from the map (after all layers are loaded)
        newLayer.onAdd = (function(map) {
            Object.getPrototypeOf(this).onAdd.call(this, map);
            this.hide();
        }).bind(newLayer);
        return newLayer;
    },

    _createLayerForTime:function(time){
        var wmsParams = this._baseLayer.options;
        wmsParams.time = new Date(time).toISOString();
        return new this._baseLayer.constructor(this._baseLayer.getURL(), wmsParams);
    },

    _getLoadedTimes: function() {
        var result = [];
        for (var prop in this._layers) {
            if (this._layers.hasOwnProperty(prop)) {
                result.push(prop);
            }
        }
        return result.sort(function(a, b) {
            return a - b;
        });
    },

    _removeLayers: function(times) {
        for (var i = 0, l = times.length; i < l; i++) {
            if (this._map)
                this._map.removeLayer(this._layers[times[i]]);
            delete this._layers[times[i]];
        }
    },

    setMinimumForwardCache: function(value) {
        if (value > this._timeCacheForward) {
            this._timeCacheForward = value;
        }
    },

    _requestTimeDimensionFromCapabilities: function() {
        if (this._capabilitiesRequested) {
            return;
        }
        this._capabilitiesRequested = true;
        var url = this._getCapabilitiesUrl();
        if (this._proxy) {
            url = this._proxy + '?url=' + encodeURIComponent(url);
        }
        var oReq = new XMLHttpRequest();
        oReq.addEventListener("load", (function(xhr) {
            var data = xhr.currentTarget.responseXML;
            this._defaultTime = Date.parse(this._getDefaultTimeFromCapabilities(data));
            this._setDefaultTime = this._setDefaultTime || (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0);
            this.setAvailableTimes(this._parseTimeDimensionFromCapabilities(data));
            if (this._setDefaultTime && this._timeDimension) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }).bind(this));
        oReq.overrideMimeType('application/xml');
        oReq.open("GET", url);
        oReq.send();
    },

    _getCapabilitiesUrl: function() {
        var url = this._baseLayer.getURL();
        if (this._getCapabilitiesAlternateUrl)
            url = this._getCapabilitiesAlternateUrl;
        var params = L.extend({}, this._getCapabilitiesParams, {
          'request': 'GetCapabilities',
          'service': 'WMS',
          'version': this._wmsVersion
        });
        url = url + L.Util.getParamString(params, url, params.uppercase);
        return url;
    },

    _parseTimeDimensionFromCapabilities: function(xml) {
        var layers = xml.querySelectorAll('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layer = null;
        var times = null;

        layers.forEach(function(current) {
            if (current.querySelector("Name").innerHTML === layerName) {
                layer = current;
            }
        })
        if (layer) {
            times = this._getTimesFromLayerCapabilities(layer);
            if (!times) {
                times = this._getTimesFromLayerCapabilities(layer.parentNode);
            }
        }

        return times;
    },

    _getTimesFromLayerCapabilities: function(layer) {
        var times = null;
        var nodes = layer.children;
        for (var i=0, l=nodes.length; i<l; i++){
            if (nodes[i].nodeName !== 'Extent' && nodes[i].nodeName !== 'Dimension') continue;
            if (nodes[i].getAttribute('name') !== 'time') continue;
            if (!nodes[i].textContent.length) continue;
            times = nodes[i].textContent.trim();
            break;
        };
        return times;
    },

    _getDefaultTimeFromCapabilities: function(xml) {
        var layers = xml.querySelectorAll('Layer[queryable="1"]');
        var layerName = this._baseLayer.wmsParams.layers;
        var layer = null;

        layers.forEach(function(current) {
            if (current.querySelector("Name").innerHTML === layerName) {
                layer = current;
            }
        })

        var defaultTime = 0;
        if (layer) {
            defaultTime = this._getDefaultTimeFromLayerCapabilities(layer);
            if (defaultTime == 0) {
                defaultTime = this._getDefaultTimeFromLayerCapabilities(layer.parentNode);
            }
        }
        return defaultTime;
    },

    _getDefaultTimeFromLayerCapabilities: function(layer) {
        var defaultTime = 0;
        var nodes = layer.children;
        for (var i=0, l=nodes.length; i<l; i++) {
            if (nodes[i].nodeName !== 'Extent' && nodes[i].nodeName !== 'Dimension') continue;
            if (nodes[i].getAttribute('name') !== 'time') continue;
            if (!nodes[i].attributes.default) continue;
            if (!nodes[i].attributes.default.textContent.length) continue;
            defaultTime = nodes[i].attributes.default.textContent.trim();
            break;
        };
        return defaultTime;
    },

    setAvailableTimes: function(times) {
        this._availableTimes = L.TimeDimension.Util.parseTimesExpression(times, this._period);
        this._updateTimeDimensionAvailableTimes();
    },

    _updateTimeDimensionAvailableTimes: function() {
        if ((this._timeDimension && this._updateTimeDimension) ||
            (this._timeDimension && this._timeDimension.getAvailableTimes().length == 0)) {
            this._timeDimension.setAvailableTimes(this._availableTimes, this._updateTimeDimensionMode);
            if (this._setDefaultTime && this._defaultTime > 0) {
                this._timeDimension.setCurrentTime(this._defaultTime);
            }
        }
    },

    _getNearestTime: function(time) {
        if (this._layers.hasOwnProperty(time)) {
            return time;
        }
        if (this._availableTimes.length == 0) {
            return time;
        }
        var index = 0;
        var len = this._availableTimes.length;
        for (; index < len; index++) {
            if (time < this._availableTimes[index]) {
                break;
            }
        }
        // We've found the first index greater than the time. Get the previous
        if (index > 0) {
            index--;
        }
        if (time != this._availableTimes[index]) {
            console.log('Search layer time: ' + new Date(time).toISOString());
            console.log('Return layer time: ' + new Date(this._availableTimes[index]).toISOString());
        }
        return this._availableTimes[index];
    },

});

if (!L.NonTiledLayer) {
    L.NonTiledLayer = (L.Layer || L.Class).extend({});
}

L.NonTiledLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.NonTiledLayer.prototype._update,
    _originalOnRemove: L.NonTiledLayer.prototype.onRemove,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    onRemove: function(map) {
        this._loaded = false;
        this._originalOnRemove(map);
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        this._div.style.display = 'none';
    },

    show: function() {
        this._visible = true;
        this._div.style.display = 'block';
    },

    getURL: function() {
        return this._wmsUrl;
    }

});

L.TileLayer.include({
    _visible: true,
    _loaded: false,

    _originalUpdate: L.TileLayer.prototype._update,

    _update: function() {
        if (!this._visible && this._loaded) {
            return;
        }
        this._originalUpdate();
    },

    setLoaded: function(loaded) {
        this._loaded = loaded;
    },

    isLoaded: function() {
        return this._loaded;
    },

    hide: function() {
        this._visible = false;
        if (this._container) {
            this._container.style.display = 'none';
        }
    },

    show: function() {
        this._visible = true;
        if (this._container) {
            this._container.style.display = 'block';
        }
    },

    getURL: function() {
        return this._url;
    }

});

L.timeDimension.layer.wms = function(layer, options) {
    return new L.TimeDimension.Layer.WMS(layer, options);
};
<|endoftext|>


<commit_before><commit_msg>Updated the static site<commit_after><|endoftext|>


<commit_before>/* eslint-disable quotes */
// WebGLRenderingContext related methods
import {WebGLRenderingContext, WebGL2RenderingContext} from '../webgl-utils';
import {createHeadlessContext} from './create-headless-context';
import {getCanvas} from './create-canvas';
import {createBrowserContext} from './create-browser-context';
import trackContextState from './track-context-state';
import {glGetDebugInfo} from './context-limits';
import queryManager from '../webgl-utils/query-manager';

import {log, isBrowser, global} from '../utils';
import assert from '../utils/assert';

// Heuristic testing of contexts (to indentify debug wrappers around gl contexts)
const GL_ARRAY_BUFFER = 0x8892;
const GL_TEXTURE_BINDING_3D = 0x806A;

export const ERR_CONTEXT = 'Invalid WebGLRenderingContext';
export const ERR_WEBGL = ERR_CONTEXT;
export const ERR_WEBGL2 = 'Requires WebGL2';

// HACK: avoid use of `gl` parameter name to defeat GL constant inliner, which invalidates check
export function isWebGL(glContext) {
  return Boolean(glContext && (
    glContext instanceof WebGLRenderingContext ||
    glContext.ARRAY_BUFFER === GL_ARRAY_BUFFER
  ));
}

// HACK: avoid use of `gl` parameter name to defeat GL constant inliner, which invalidates check
export function isWebGL2(glContext) {
  return Boolean(glContext && (
    glContext instanceof WebGL2RenderingContext ||
    glContext.TEXTURE_BINDING_3D === GL_TEXTURE_BINDING_3D
  ));
}

export function assertWebGLContext(gl) {
  // Need to handle debug context
  assert(isWebGL(gl), ERR_CONTEXT);
}

export function assertWebGL2Context(gl) {
  // Need to handle debug context
  assert(isWebGL2(gl), ERR_WEBGL2);
}

const contextDefaults = {
  // COMMON CONTEXT PARAMETERS
  // Attempt to allocate WebGL2 context
  webgl2: true, // Attempt to create a WebGL2 context (false to force webgl1)
  webgl1: true,  // Attempt to create a WebGL1 context (false to fail if webgl2 not available)
  throwOnFailure: true,
  manageState: true,
  // BROWSER CONTEXT PARAMETERS
  canvas: null, // A canvas element or a canvas string id
  debug: false, // Instrument context (at the expense of performance)
  // HEADLESS CONTEXT PARAMETERS
  width: 800, // width are height are only used by headless gl
  height: 600
  // WEBGL/HEADLESS CONTEXT PARAMETERS
  // Remaining options are passed through to context creator
};

/*
 * Change default context creation parameters.
 * Main use case is regression test suite.
 */
export function setContextDefaults(opts = {}) {
  Object.assign(contextDefaults, {width: 1, height: 1}, opts);
}

/*
 * Creates a context giving access to the WebGL API
 */
/* eslint-disable complexity, max-statements */
export function createGLContext(opts = {}) {
  opts = Object.assign({}, contextDefaults, opts);
  const {canvas, width, height, throwOnError, manageState, debug} = opts;

  // Error reporting function, enables exceptions to be disabled
  function onError(message) {
    if (throwOnError) {
      throw new Error(message);
    }
    // log.log(0, message)();
    return null;
  }

  let gl;
  if (isBrowser) {
    // Get or create a canvas
    const targetCanvas = getCanvas({canvas, width, height, onError});
    // Create a WebGL context in the canvas
    gl = createBrowserContext({canvas: targetCanvas, opts});
  } else {
    // Create a headless-gl context under Node.js
    gl = createHeadlessContext({width, height, opts, onError});
  }
  if (!gl) {
    return null;
  }

  // Install context state tracking
  if (manageState) {
    trackContextState(gl, {
      copyState: false,
      log: (...args) => log.log(1, ...args)()
    });
  }

  // Add debug instrumentation to the context
  if (isBrowser && debug) {
    if (!global.makeDebugContext) {
      log.warn('WebGL debug mode not activated. import "@luma.gl/debug" to enable.')();
    } else {
      gl = global.makeDebugContext(gl, {debug});
      // Debug forces log level to at least 1
      log.priority = Math.max(log.priority, 1);
    }
  }

  // Log some debug info about the newly created context
  logInfo(gl);

  // Add to seer integration
  return gl;
}

export function destroyGLContext(gl) {
  // TODO - Remove from seer integration

  // TODO - Unregister any tracking/polyfills

  // There is no way to delete browser based context

  // Destroy headless gl context
  const ext = gl.getExtension('STACKGL_destroy_context');
  if (ext) {
    ext.destroy();
  }
}

/**
 * Resize the canvas' drawing buffer.
 *
 * Can match the canvas CSS size, and optionally also consider devicePixelRatio
 * Can be called every frame
 *
 * Regardless of size, the drawing buffer will always be scaled to the viewport, but
 * for best visual results, usually set to either:
 *  canvas CSS width x canvas CSS height
 *  canvas CSS width * devicePixelRatio x canvas CSS height * devicePixelRatio
 * See http://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
 *
 * resizeGLContext(gl, {width, height, useDevicePixels})
 */
export function resizeGLContext(gl, opts = {}) {
  // Resize browser context
  if (gl.canvas) {
    /* global window */
    const devicePixelRatio = opts.useDevicePixels ? window.devicePixelRatio || 1 : 1;

    const width = `width` in opts ? opts.width : gl.canvas.clientWidth;
    const height = `height` in opts ? opts.height : gl.canvas.clientHeight;

    gl.canvas.width = width * devicePixelRatio;
    gl.canvas.height = height * devicePixelRatio;

    return;
  }

  // Resize headless gl context
  const ext = gl.getExtension('STACKGL_resize_drawingbuffer');
  if (ext && `width` in opts && `height` in opts) {
    ext.resize(opts.width, opts.height);
  }
}

// POLLING FOR PENDING QUERIES
// Calling this function checks all pending queries for completion
export function pollGLContext(gl) {
  queryManager.poll(gl);
}

// HELPER METHODS

function logInfo(gl) {
  const webGL = isWebGL2(gl) ? 'WebGL2' : 'WebGL1';
  const info = glGetDebugInfo(gl);
  const driver = info ? `(${info.vendor},${info.renderer})` : '';
  const debug = gl.debug ? ' debug' : '';
  log.once(0, `${webGL}${debug} context ${driver}`)();
}
<commit_msg>Fix a crash (#824)<commit_after>/* eslint-disable quotes */
// WebGLRenderingContext related methods
import {WebGLRenderingContext, WebGL2RenderingContext} from '../webgl-utils';
import {createHeadlessContext} from './create-headless-context';
import {getCanvas} from './create-canvas';
import {createBrowserContext} from './create-browser-context';
import trackContextState from './track-context-state';
import {glGetDebugInfo} from './context-limits';
import queryManager from '../webgl-utils/query-manager';

import {log, isBrowser} from '../utils';
import {global} from '../utils/globals';
import assert from '../utils/assert';

// Heuristic testing of contexts (to indentify debug wrappers around gl contexts)
const GL_ARRAY_BUFFER = 0x8892;
const GL_TEXTURE_BINDING_3D = 0x806A;

export const ERR_CONTEXT = 'Invalid WebGLRenderingContext';
export const ERR_WEBGL = ERR_CONTEXT;
export const ERR_WEBGL2 = 'Requires WebGL2';

// HACK: avoid use of `gl` parameter name to defeat GL constant inliner, which invalidates check
export function isWebGL(glContext) {
  return Boolean(glContext && (
    glContext instanceof WebGLRenderingContext ||
    glContext.ARRAY_BUFFER === GL_ARRAY_BUFFER
  ));
}

// HACK: avoid use of `gl` parameter name to defeat GL constant inliner, which invalidates check
export function isWebGL2(glContext) {
  return Boolean(glContext && (
    glContext instanceof WebGL2RenderingContext ||
    glContext.TEXTURE_BINDING_3D === GL_TEXTURE_BINDING_3D
  ));
}

export function assertWebGLContext(gl) {
  // Need to handle debug context
  assert(isWebGL(gl), ERR_CONTEXT);
}

export function assertWebGL2Context(gl) {
  // Need to handle debug context
  assert(isWebGL2(gl), ERR_WEBGL2);
}

const contextDefaults = {
  // COMMON CONTEXT PARAMETERS
  // Attempt to allocate WebGL2 context
  webgl2: true, // Attempt to create a WebGL2 context (false to force webgl1)
  webgl1: true,  // Attempt to create a WebGL1 context (false to fail if webgl2 not available)
  throwOnFailure: true,
  manageState: true,
  // BROWSER CONTEXT PARAMETERS
  canvas: null, // A canvas element or a canvas string id
  debug: false, // Instrument context (at the expense of performance)
  // HEADLESS CONTEXT PARAMETERS
  width: 800, // width are height are only used by headless gl
  height: 600
  // WEBGL/HEADLESS CONTEXT PARAMETERS
  // Remaining options are passed through to context creator
};

/*
 * Change default context creation parameters.
 * Main use case is regression test suite.
 */
export function setContextDefaults(opts = {}) {
  Object.assign(contextDefaults, {width: 1, height: 1}, opts);
}

/*
 * Creates a context giving access to the WebGL API
 */
/* eslint-disable complexity, max-statements */
export function createGLContext(opts = {}) {
  opts = Object.assign({}, contextDefaults, opts);
  const {canvas, width, height, throwOnError, manageState, debug} = opts;

  // Error reporting function, enables exceptions to be disabled
  function onError(message) {
    if (throwOnError) {
      throw new Error(message);
    }
    // log.log(0, message)();
    return null;
  }

  let gl;
  if (isBrowser) {
    // Get or create a canvas
    const targetCanvas = getCanvas({canvas, width, height, onError});
    // Create a WebGL context in the canvas
    gl = createBrowserContext({canvas: targetCanvas, opts});
  } else {
    // Create a headless-gl context under Node.js
    gl = createHeadlessContext({width, height, opts, onError});
  }
  if (!gl) {
    return null;
  }

  // Install context state tracking
  if (manageState) {
    trackContextState(gl, {
      copyState: false,
      log: (...args) => log.log(1, ...args)()
    });
  }

  // Add debug instrumentation to the context
  if (isBrowser && debug) {
    if (!global.makeDebugContext) {
      log.warn('WebGL debug mode not activated. import "@luma.gl/debug" to enable.')();
    } else {
      gl = global.makeDebugContext(gl, {debug});
      // Debug forces log level to at least 1
      log.priority = Math.max(log.priority, 1);
    }
  }

  // Log some debug info about the newly created context
  logInfo(gl);

  // Add to seer integration
  return gl;
}

export function destroyGLContext(gl) {
  // TODO - Remove from seer integration

  // TODO - Unregister any tracking/polyfills

  // There is no way to delete browser based context

  // Destroy headless gl context
  const ext = gl.getExtension('STACKGL_destroy_context');
  if (ext) {
    ext.destroy();
  }
}

/**
 * Resize the canvas' drawing buffer.
 *
 * Can match the canvas CSS size, and optionally also consider devicePixelRatio
 * Can be called every frame
 *
 * Regardless of size, the drawing buffer will always be scaled to the viewport, but
 * for best visual results, usually set to either:
 *  canvas CSS width x canvas CSS height
 *  canvas CSS width * devicePixelRatio x canvas CSS height * devicePixelRatio
 * See http://webgl2fundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
 *
 * resizeGLContext(gl, {width, height, useDevicePixels})
 */
export function resizeGLContext(gl, opts = {}) {
  // Resize browser context
  if (gl.canvas) {
    /* global window */
    const devicePixelRatio = opts.useDevicePixels ? window.devicePixelRatio || 1 : 1;

    const width = `width` in opts ? opts.width : gl.canvas.clientWidth;
    const height = `height` in opts ? opts.height : gl.canvas.clientHeight;

    gl.canvas.width = width * devicePixelRatio;
    gl.canvas.height = height * devicePixelRatio;

    return;
  }

  // Resize headless gl context
  const ext = gl.getExtension('STACKGL_resize_drawingbuffer');
  if (ext && `width` in opts && `height` in opts) {
    ext.resize(opts.width, opts.height);
  }
}

// POLLING FOR PENDING QUERIES
// Calling this function checks all pending queries for completion
export function pollGLContext(gl) {
  queryManager.poll(gl);
}

// HELPER METHODS

function logInfo(gl) {
  const webGL = isWebGL2(gl) ? 'WebGL2' : 'WebGL1';
  const info = glGetDebugInfo(gl);
  const driver = info ? `(${info.vendor},${info.renderer})` : '';
  const debug = gl.debug ? ' debug' : '';
  log.once(0, `${webGL}${debug} context ${driver}`)();
}
<|endoftext|>


<commit_before>// DecentCMS (c) 2014 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

// TODO: morph this so this enables sequential rendering while maintaining asynchronicity.

var stream = require('stream');
var Transform = stream.Transform;
var util = require('util');
var html = require('htmlencode');

util.inherits(RenderStream, Transform);

/**
 * @description
 * A transform stream for rendering shapes.
 * @param scope
 * @param [options.contentManager]
 * @constructor
 */
function RenderStream(scope, options) {
  Transform.call(this, {
    objectMode: true
  });
  options = options || {};
  this.scope = scope;
  this.scripts = [];
  this.stylesheets = [];
  this.meta = {};
  this.title = options.title || '';
  this.tags = [];
  this.contentManager = options.contentManager;
};
RenderStream.feature = 'render-stream';

RenderStream.prototype._transform = function transform(chunk, encoding, next) {
  this.push(chunk);
  next();
};

/**
 * @description
 * Writes a string, unencoded.
 * @param {string} text The text to write.
 */
RenderStream.prototype.write = function write(text) {
  this.push(text);
  return this;
};

/**
 * @description
 * Writes the string after HTML-encoding it.
 * @param {string} text The string to write.
 */
RenderStream.prototype.writeEncoded = function writeEncoded(text) {
  this.write(html.htmlEncode(text));
  return this;
};

/**
 * @description
 * Writes the string, followed by \r\n.
 * @param {string} [text] The string to write.
 */
RenderStream.prototype.writeLine = function writeLine(text) {
  if (text) {this.write(text);}
  this.write('\r\n');
  return this;
}

/**
 * @description
 * Writes the string after HTML-encoding it, followed by \r\n.
 * @param {string} [text] The string to write.
 */
RenderStream.prototype.writeEncodedLine = function writeEncodedLine(text) {
  if (text) {this.writeEncoded(text);}
  this.write('\r\n');
  return this;
};

/**
 * @description
 * Renders a
 tag.
 */
RenderStream.prototype.br = function br() {
  this.write('
\r\n');
  return this;
};

/**
 * @description
 * Renders a tag.
 * @param {string} tagName      The name of the tag.
 * @param {object} [attributes] Attributes to add to the tag.
 * @param {string} [content]    Text content of the tag.
 */
RenderStream.prototype.tag = function tag(tagName, attributes, content) {
  if (typeof content === 'string') {
    this.startTag(tagName, attributes);
    this.writeEncoded(content);
    this.endTag();
  }
  else {
    this.write('<' + tagName);
    this.renderAttributes(attributes);
    this.write('/>');
  }
  return this;
};

/**
 * @description
 * Renders attributes as name="encoded value".
 * @param {object} attributes The attributes to render.
 */
RenderStream.prototype.renderAttributes = function renderAttributes(attributes) {
  for (var attributeName in attributes) {
    this.write(' ' + attributeName + '="');
    this.writeEncoded(attributes[attributeName]);
    this.write('"');
  }
  return this;
};

/**
 * @description
 * Writes the a start tag, and pushes the tag on a stack in order
 * for endTag to know what tag to close.
 * @param {string} tagName      The name of the tag.
 * @param {object} [attributes] The attributes of the tag.
 */
RenderStream.prototype.startTag = function startTag(tagName, attributes) {
  this.write('<' + tagName);
  this.renderAttributes(attributes);
  this.write('>');
  this.tags.push(tagName);
  return this;
};

/**
 * @description
 * Writes the end tag for the latest unclosed tag started.
 */
RenderStream.prototype.endTag = function endTag() {
  var tagName = this.tags.pop();
  this.write('</' + tagName + '>');
  return this;
};

/**
 * @description
 * Closes all pending tags.
 */
RenderStream.prototype.endAllTags = function endAllTags() {
  while (this.tags.length) {
    this.endTag();
  }
  return this;
};

/**
 * @description
 * Renders a doctype.
 * @param {string} [type] The doctype. 'html' if not specified.
 */
RenderStream.prototype.doctype = function doctype(type) {
  this.write('<!DOCTYPE ' + (type || 'html') + '>');
};

// TODO: handle minimized files

/**
 * @description
 * Adds a style sheet to the list of style sheets.
 * The style sheet name will resolve to a file in the theme, or in
 * modules, under a css folder.
 * @param {string} name The name of a local style sheet file.
 */
RenderStream.prototype.addStyleSheet = function addStyleSheet(name) {
  var url = '/css/' + name + '.css';
  this.addExternalStyleSheet(url);
  return this;
};

/**
 * @description
 * Adds an external style sheet to the list of style sheets.
 * @param {string} url The URL of an external style sheet file.
 */
RenderStream.prototype.addExternalStyleSheet = function addExternalStyleSheet(url) {
  if (!(url in this.stylesheets)) {
    this.stylesheets.push(url);
  }
  return this;
};

/**
 * @description
 * Renders all style sheets that have been registered so far using
 * addStyleSheet and addExternalStyleSheet.
 */
RenderStream.prototype.renderStyleSheets = function renderStyleSheets() {
  for (var i = 0; i < this.stylesheets.length; i++) {
    this.write('  ');
    this.tag('link', {
      href: this.stylesheets[i],
      rel: 'stylesheet',
      type: 'text/css'
    });
    this.writeLine();
  }
  return this;
};

/**
 * @description
 * Adds a script to the list of scripts.
 * The script name will resolve to a file in the theme, or in
 * modules, under a js folder.
 * @param {string} name The name of a local script file.
 */
RenderStream.prototype.addScript = function addScript(name) {
  var url = '/js/' + name + '.js';
  this.addExternalScript(url);
  return this;
};

/**
 * @description
 * Adds an external script to the list of scripts.
 * @param {string} url The URL of an external script file.
 */
RenderStream.prototype.addExternalScript = function addExternalScript(url) {
  if (!(url in this.scripts)) {
    this.scripts.push(url);
  }
  return this;
};

/**
 * @description
 * Renders all scripts that have been registered so far using
 * addScript and addExternalScript.
 */
RenderStream.prototype.renderScripts = function renderScripts() {
  for (var i = 0; i < this.scripts.length; i++) {
    this.write('  ');
    this.tag('script', {
      src: this.scripts[i],
      type: 'text/javascript'
    }, '');
    this.writeLine();
  }
  return this;
};

/**
 * @description
 * Adds a meta tag. This does not render the tag, it just stores the data for,
 * typically, the layout template to render using renderMeta.
 * @param {string} name         The name of the meta tag (will be rendered as the name attribute).
 * @param {string} value        The value of the meta tag (will be rendered as the content attribute).
 * @param {object} [attributes] Additional attributes to add to the meta tag.
 */
RenderStream.prototype.addMeta = function addMeta(name, value, attributes) {
  this.meta[name] = {
    name: name,
    value: value,
    attributes: attributes
  };
  return this;
};

/**
 * @description
 * Renders the meta tag that have been registered so far using addMeta.
 */
RenderStream.prototype.renderMeta = function renderMeta() {
  for (var name in this.meta) {
    var meta = this.meta[name];
    var attributes = meta.attributes || {};
    attributes.name = meta.name;
    attributes.content = meta.value;
    this.write('  ');
    this.tag('meta', attributes);
    this.writeLine();
  }
  return this;
};

// TODO: add API to render a shape, that internally triggers the rendering event

module.exports = RenderStream;<commit_msg>Doctype method on render stream is fluent.<commit_after>// DecentCMS (c) 2014 Bertrand Le Roy, under MIT. See LICENSE.txt for licensing details.
'use strict';

// TODO: morph this so this enables sequential rendering while maintaining asynchronicity.

var stream = require('stream');
var Transform = stream.Transform;
var util = require('util');
var html = require('htmlencode');

util.inherits(RenderStream, Transform);

/**
 * @description
 * A transform stream for rendering shapes.
 * @param scope
 * @param [options.contentManager]
 * @constructor
 */
function RenderStream(scope, options) {
  Transform.call(this, {
    objectMode: true
  });
  options = options || {};
  this.scope = scope;
  this.scripts = [];
  this.stylesheets = [];
  this.meta = {};
  this.title = options.title || '';
  this.tags = [];
  this.contentManager = options.contentManager;
};
RenderStream.feature = 'render-stream';

RenderStream.prototype._transform = function transform(chunk, encoding, next) {
  this.push(chunk);
  next();
};

/**
 * @description
 * Writes a string, unencoded.
 * @param {string} text The text to write.
 */
RenderStream.prototype.write = function write(text) {
  this.push(text);
  return this;
};

/**
 * @description
 * Writes the string after HTML-encoding it.
 * @param {string} text The string to write.
 */
RenderStream.prototype.writeEncoded = function writeEncoded(text) {
  this.write(html.htmlEncode(text));
  return this;
};

/**
 * @description
 * Writes the string, followed by \r\n.
 * @param {string} [text] The string to write.
 */
RenderStream.prototype.writeLine = function writeLine(text) {
  if (text) {this.write(text);}
  this.write('\r\n');
  return this;
}

/**
 * @description
 * Writes the string after HTML-encoding it, followed by \r\n.
 * @param {string} [text] The string to write.
 */
RenderStream.prototype.writeEncodedLine = function writeEncodedLine(text) {
  if (text) {this.writeEncoded(text);}
  this.write('\r\n');
  return this;
};

/**
 * @description
 * Renders a
 tag.
 */
RenderStream.prototype.br = function br() {
  this.write('
\r\n');
  return this;
};

/**
 * @description
 * Renders a tag.
 * @param {string} tagName      The name of the tag.
 * @param {object} [attributes] Attributes to add to the tag.
 * @param {string} [content]    Text content of the tag.
 */
RenderStream.prototype.tag = function tag(tagName, attributes, content) {
  if (typeof content === 'string') {
    this.startTag(tagName, attributes);
    this.writeEncoded(content);
    this.endTag();
  }
  else {
    this.write('<' + tagName);
    this.renderAttributes(attributes);
    this.write('/>');
  }
  return this;
};

/**
 * @description
 * Renders attributes as name="encoded value".
 * @param {object} attributes The attributes to render.
 */
RenderStream.prototype.renderAttributes = function renderAttributes(attributes) {
  for (var attributeName in attributes) {
    this.write(' ' + attributeName + '="');
    this.writeEncoded(attributes[attributeName]);
    this.write('"');
  }
  return this;
};

/**
 * @description
 * Writes the a start tag, and pushes the tag on a stack in order
 * for endTag to know what tag to close.
 * @param {string} tagName      The name of the tag.
 * @param {object} [attributes] The attributes of the tag.
 */
RenderStream.prototype.startTag = function startTag(tagName, attributes) {
  this.write('<' + tagName);
  this.renderAttributes(attributes);
  this.write('>');
  this.tags.push(tagName);
  return this;
};

/**
 * @description
 * Writes the end tag for the latest unclosed tag started.
 */
RenderStream.prototype.endTag = function endTag() {
  var tagName = this.tags.pop();
  this.write('</' + tagName + '>');
  return this;
};

/**
 * @description
 * Closes all pending tags.
 */
RenderStream.prototype.endAllTags = function endAllTags() {
  while (this.tags.length) {
    this.endTag();
  }
  return this;
};

/**
 * @description
 * Renders a doctype.
 * @param {string} [type] The doctype. 'html' if not specified.
 */
RenderStream.prototype.doctype = function doctype(type) {
  this.write('<!DOCTYPE ' + (type || 'html') + '>');
  return this;
};

// TODO: handle minimized files

/**
 * @description
 * Adds a style sheet to the list of style sheets.
 * The style sheet name will resolve to a file in the theme, or in
 * modules, under a css folder.
 * @param {string} name The name of a local style sheet file.
 */
RenderStream.prototype.addStyleSheet = function addStyleSheet(name) {
  var url = '/css/' + name + '.css';
  this.addExternalStyleSheet(url);
  return this;
};

/**
 * @description
 * Adds an external style sheet to the list of style sheets.
 * @param {string} url The URL of an external style sheet file.
 */
RenderStream.prototype.addExternalStyleSheet = function addExternalStyleSheet(url) {
  if (!(url in this.stylesheets)) {
    this.stylesheets.push(url);
  }
  return this;
};

/**
 * @description
 * Renders all style sheets that have been registered so far using
 * addStyleSheet and addExternalStyleSheet.
 */
RenderStream.prototype.renderStyleSheets = function renderStyleSheets() {
  for (var i = 0; i < this.stylesheets.length; i++) {
    this.write('  ');
    this.tag('link', {
      href: this.stylesheets[i],
      rel: 'stylesheet',
      type: 'text/css'
    });
    this.writeLine();
  }
  return this;
};

/**
 * @description
 * Adds a script to the list of scripts.
 * The script name will resolve to a file in the theme, or in
 * modules, under a js folder.
 * @param {string} name The name of a local script file.
 */
RenderStream.prototype.addScript = function addScript(name) {
  var url = '/js/' + name + '.js';
  this.addExternalScript(url);
  return this;
};

/**
 * @description
 * Adds an external script to the list of scripts.
 * @param {string} url The URL of an external script file.
 */
RenderStream.prototype.addExternalScript = function addExternalScript(url) {
  if (!(url in this.scripts)) {
    this.scripts.push(url);
  }
  return this;
};

/**
 * @description
 * Renders all scripts that have been registered so far using
 * addScript and addExternalScript.
 */
RenderStream.prototype.renderScripts = function renderScripts() {
  for (var i = 0; i < this.scripts.length; i++) {
    this.write('  ');
    this.tag('script', {
      src: this.scripts[i],
      type: 'text/javascript'
    }, '');
    this.writeLine();
  }
  return this;
};

/**
 * @description
 * Adds a meta tag. This does not render the tag, it just stores the data for,
 * typically, the layout template to render using renderMeta.
 * @param {string} name         The name of the meta tag (will be rendered as the name attribute).
 * @param {string} value        The value of the meta tag (will be rendered as the content attribute).
 * @param {object} [attributes] Additional attributes to add to the meta tag.
 */
RenderStream.prototype.addMeta = function addMeta(name, value, attributes) {
  this.meta[name] = {
    name: name,
    value: value,
    attributes: attributes
  };
  return this;
};

/**
 * @description
 * Renders the meta tag that have been registered so far using addMeta.
 */
RenderStream.prototype.renderMeta = function renderMeta() {
  for (var name in this.meta) {
    var meta = this.meta[name];
    var attributes = meta.attributes || {};
    attributes.name = meta.name;
    attributes.content = meta.value;
    this.write('  ');
    this.tag('meta', attributes);
    this.writeLine();
  }
  return this;
};

// TODO: add API to render a shape, that internally triggers the rendering event

module.exports = RenderStream;<|endoftext|>


<commit_before>'use strict';

// Karma
module.exports = function(config) {
	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['jasmine'],

		// list of files / patterns to load in the browser
		files: [
			//'app/bower_components/jquery/jquery.js',
			'app/bower_components/angular/angular.js',
			'app/bower_components/angular-mocks/angular-*.js',
			'app/bower_components/angular-route/angular-route.js',
			'app/bower_components/angular-resource/angular-resource.js',
			'app/bower_components/angular-socket.io-mock.js',
			/*'app/bower_components/angular-sanitize/angular-sanitize.js',
			'app/bower_components/angular-cookies/angular-cookies.js',*/
			//'app/bower_components/underscore/underscore.js',
			'app/norris-nrti.js',
			'app/Model/*.js',
			'app/Model/GraphsModel/*.js',
			'app/Model/FlowsModel/*.js',
			'app/Model/PagesModel/*.js',
			'app/View/*.js',
			'app/Controller/*.js',
			'test/unit/Model/*.js',
			'test/unit/Controller/*.js',
			'test/unit/View/*.js'
		],

		// list of files to exclude
		exclude: [

		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			// source files, that you wanna generate coverage for
			// do not include tests or libraries
			// (these files will be instrumented by Istanbul)
			'app/app.js': ['coverage'],
			'app/Model/*.js': ['coverage'],
			'app/Model/GraphsModel/*.js': ['coverage'],
			'app/Model/FlowsModel/*.js': ['coverage'],
			'app/Model/PagesModel/*.js': ['coverage'],
			'app/View/*.js': ['coverage'],
			'app/Controllers/*.js': ['coverage']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['progress', 'junit', 'coverage'],

		junitReporter: {
			outputFile: '../gh-pages/reports-frontend/test-result.xml'
		},

		// configure the reporter
		coverageReporter: {
			dir: '../gh-pages/reports-frontend/coverage/',
			reporters:[
		        {
		        	type: 'text',
		        	subdir: '.',
		        	file: 'text.txt'
		        },
		        {
		        	type: 'text-summary',
		        	subdir: '.',
		        	file: 'text-summary.txt'
		        },
				{
					type: 'lcov',
					subdir: '.',
					suite: 'frontend'
				}
			],
		},

		// web server port
		port: 15077,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_DEBUG,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: false,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['PhantomJS'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true
	});
};<commit_msg>some fixes<commit_after>'use strict';

// Karma
module.exports = function(config) {
	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['jasmine'],

		// list of files / patterns to load in the browser
		files: [
			//'app/bower_components/jquery/jquery.js',
			'app/bower_components/angular/angular.js',
			'app/bower_components/angular-mocks/angular-*.js',
			'app/bower_components/angular-route/angular-route.js',
			'app/bower_components/angular-resource/angular-resource.js',
			'app/bower_components/angular-socket.io-mock.js',
			/*'app/bower_components/angular-sanitize/angular-sanitize.js',
			'app/bower_components/angular-cookies/angular-cookies.js',*/
			//'app/bower_components/underscore/underscore.js',
			'app/norris-nrti.js',
			'app/Model/*.js',
			'app/Model/GraphsModel/*.js',
			'app/Model/FlowsModel/*.js',
			'app/Model/PagesModel/*.js',
			'app/View/*.js',
			'app/Controller/*.js',
			'app/Controller/GraphsControllers/*.js',
			'test/unit/Model/*.js',
			'test/unit/Controller/*.js',
			'test/unit/View/*.js'
		],

		// list of files to exclude
		exclude: [

		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			// source files, that you wanna generate coverage for
			// do not include tests or libraries
			// (these files will be instrumented by Istanbul)
			'app/app.js': ['coverage'],
			'app/Model/*.js': ['coverage'],
			'app/Model/GraphsModel/*.js': ['coverage'],
			'app/Model/FlowsModel/*.js': ['coverage'],
			'app/Model/PagesModel/*.js': ['coverage'],
			'app/View/*.js': ['coverage'],
			'app/Controllers/*.js': ['coverage']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['progress', 'junit', 'coverage'],

		junitReporter: {
			outputFile: '../gh-pages/reports-frontend/test-result.xml'
		},

		// configure the reporter
		coverageReporter: {
			dir: '../gh-pages/reports-frontend/coverage/',
			reporters:[
		        {
		        	type: 'text',
		        	subdir: '.',
		        	file: 'text.txt'
		        },
		        {
		        	type: 'text-summary',
		        	subdir: '.',
		        	file: 'text-summary.txt'
		        },
				{
					type: 'lcov',
					subdir: '.',
					suite: 'frontend'
				}
			],
		},

		// web server port
		port: 15077,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_DEBUG,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: false,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['PhantomJS'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true
	});
};<|endoftext|>


<commit_before>import React from "react";
import FontAwesome from "react-fontawesome";
import "font-awesome/css/font-awesome.css";
import { Link } from "react-router-dom";

import {
  toDate,
  isBefore,
  formatDistance,
  formatDistanceStrict,
  differenceInSeconds,
  differenceInMilliseconds
} from "date-fns";
import parseISO from "date-fns/parseISO";

export function parseISODate(input) {
  return toDate(parseISO(input));
}

export const Loading = () => (

      <FontAwesome name="cog" spin size="5x" />
      Loading...

);

export const DisplayDate = ({ date }) => {
  if (date === null) {
    throw new Error("date is null");
  }
  const dateObj = parseISODate(date);
  const now = new Date();
  if (isBefore(dateObj, now)) {
    return {formatDistance(dateObj, now)} ago;
  } else {
    return in {formatDistance(dateObj, now)};
  }
};

export const DisplayDateDifference = ({ from, to, suffix = "" }) => {
  const fromObj = parseISODate(from);
  const toObj = parseISODate(to);
  const secDiff = differenceInSeconds(toObj, fromObj);
  if (secDiff === 0) {
    const msecDiff = differenceInMilliseconds(toObj, fromObj);
    if (msecDiff > 0) {
      return (
        <span title={`From ${fromObj} to ${toObj}`}>
          {msecDiff} ms
          {suffix && ` ${suffix}`}

      );
    }
  }
  return (
    <span title={`From ${fromObj} to ${toObj}`}>
      {formatDistanceStrict(fromObj, toObj)}
      {suffix && ` ${suffix}`}

  );
};

export const thousandFormat = x => {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};

export const formatFileSize = (bytes, decimals = 0) => {
  if (!bytes) return "0 bytes";
  var k = 1024;
  var dm = decimals + 1 || 3;
  var sizes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  var i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
};

export const formatSeconds = seconds => {
  if (seconds < 1) {
    // milliseconds
    return (seconds * 1000).toFixed(0) + "ms";
  } else if (seconds > 3000) {
    return (seconds / 60).toFixed(1) + "m";
  } else if (seconds < 60) {
    return seconds.toFixed(1) + "s";
  } else {
    const minutes = Math.floor(seconds / 60);
    seconds = Math.round(seconds % 60);
    return `${minutes}m${seconds}s`;
  }
};
export const BooleanIcon = bool => (

);

export const Pagination = ({
  location,
  total,
  batchSize,
  currentPage,
  updateFilter
}) => {
  if (!currentPage) {
    currentPage = 1;
  } else if (typeof currentPage === "string") {
    currentPage = parseInt(currentPage, 10);
  }

  const nextPageUrl = page => {
    const searchParams = new URLSearchParams(location.search);
    searchParams.set("page", page);
    return location.pathname + "?" + searchParams.toString();
  };

  const goTo = (event, page) => {
    event.preventDefault();
    updateFilter({ page });
  };

  const isOverflow = page => {
    // return true if doesn't make sense to go to this page
    return page < 1 || (page - 1) * batchSize >= total;
  };

  return (

      <Link
        className="pagination-previous"
        to={nextPageUrl(currentPage - 1)}
        onClick={e => goTo(e, currentPage - 1)}
        disabled={isOverflow(currentPage - 1)}
      >
        Previous
      </Link>
      <Link
        to={nextPageUrl(currentPage + 1)}
        className="pagination-next"
        onClick={e => goTo(e, currentPage + 1)}
        disabled={isOverflow(currentPage + 1)}
      >
        Next page
      </Link>

  );
};

export const TableSubTitle = ({ total, page, batchSize }) => {
  if (total === null) {
    return null;
  }
  page = page || 1;
  const totalPages = Math.ceil(total / batchSize);
  return (

      {thousandFormat(total)} Found (Page {thousandFormat(page)} of{" "}
      {thousandFormat(totalPages)})

  );
};

export const pluralize = (number, singular, plural) => {
  if (number === 1) {
    return `1 ${singular}`;
  } else {
    return `${number} ${plural}`;
  }
};

export const DisplayFilesSummary = (files, incomplete, skipped, ignored) => {
  const sentences = [];
  sentences.push(pluralize(files, "file uploaded", "files uploaded"));
  if (incomplete) {
    sentences.push(
      pluralize(incomplete, "file incomplete", "files incomplete")
    );
  }
  sentences.push(`${skipped} skipped`);
  // Currently ignoring the 'ignored'
  return sentences.join(". ") + ".";
};

export const ShowValidationErrors = ({ errors, resetAndReload }) => {
  return (

      <button className="delete" onClick={resetAndReload} />

Filter validation errors

        {Object.keys(errors).map(key => {
          return (

              {key} - {errors[key]}

          );
        })}

  );
};

export const parseQueryString = qs => {
  const searchParams = new URLSearchParams(qs);
  const parsed = {};
  // NOTE(willkg): babel-eslint 10.0.2 erroneously kicks up this error. Once we
  // upgrade to 10.0.3, we can get rid of this.
  /* eslint-disable no-unused-vars */
  for (let [key, value] of searchParams) {
    const already = parsed[key];
    if (already === undefined) {
      parsed[key] = value;
    } else if (Array.isArray(already)) {
      parsed[key].push(value);
    } else {
      parsed[key] = [already, value];
    }
  }
  /* eslint-enable no-unused-vars */
  return parsed;
};

export const filterToQueryString = (filterObj, overrides) => {
  const copy = Object.assign(overrides || {}, filterObj);
  const searchParams = new URLSearchParams();
  Object.entries(copy).forEach(([key, value]) => {
    if (Array.isArray(value) && value.length) {
      value.forEach(v => searchParams.append(key, v));
    } else if (value) {
      searchParams.set(key, value);
    }
  });
  searchParams.sort();
  return searchParams.toString();
};

const URLTag = url => {url};

export const ShowUploadMetadata = ({ upload }) => {
  return (

          User
          {upload.user.email}

          Size
          {formatFileSize(upload.size)}

          Filename
          {upload.filename}

          Try Symbols
          {upload.try_symbols ? "Yes" : "No"}

          Download URL

            {upload.download_url ? URLTag(upload.download_url) : null}

        {upload.download_url && (

            Redirect URLs

              {!upload.redirect_urls.length && n/a}
              {upload.redirect_urls.length ? (

{URLTag(upload.download_url)}

                  {upload.redirect_urls.map(url => (

{URLTag(url)}

                  ))}

              ) : null}

        )}

          Bucket Name
          {upload.bucket_name}

          Bucket Region
          {upload.bucket_region ? upload.bucket_region : null}

          Bucket Endpoint URL

            {upload.bucket_endpoint_url ? (
              upload.bucket_endpoint_url
            ) : (
              null
            )}

          Uploaded

            <DisplayDate date={upload.created_at} />

            Completed

            {upload.completed_at ? (
              <DisplayDate date={upload.completed_at} />
            ) : (

                Incomplete!{" "}
                <DisplayIncompleteRatio files={upload.file_uploads} />

            )}
            {upload.completed_at ? (

                {" "}
                (took{" "}
                <DisplayDateDifference
                  from={upload.created_at}
                  to={upload.completed_at}
                />
                )

            ) : null}

  );
};

const DisplayIncompleteRatio = ({ files }) => {
  // Return a string like "56 of 103 (54%)" to mean that 56 of 103 files are complete.
  const all = files.length;
  const complete = files.filter(file => !!file.completed_at).length;
  const percentage = (100 * complete) / all;
  return `${complete} of ${all} (${Math.trunc(percentage)}%)`;
};

export const ShowFileMetadata = ({ file }) => (

        Key
        {file.key}

        Size
        {formatFileSize(file.size)}

        Bucket Name
        {file.bucket_name}

        Update
        {BooleanIcon(file.update)}

        Compressed
        {BooleanIcon(file.compressed)}

        Uploaded

          <DisplayDate date={file.created_at} />

        Completed

          {file.completed_at ? (
            <DisplayDate date={file.completed_at} />
          ) : (
            Incomplete!
          )}
          {file.completed_at ? (

              {" "}
              (took{" "}
              <DisplayDateDifference
                from={file.created_at}
                to={file.completed_at}
              />
              )

          ) : null}

);

export const ShowMicrosoftDownloadMetadata = ({ download }) => (

        URL
        {download.url}

        Error

          {download.error ? (
            {download.error}
          ) : (
            "-"
          )}

        Created

          <DisplayDate date={download.created_at} />

        Completed

          {download.completed_at ? (
            <DisplayDate date={download.completed_at} />
          ) : (
            Incomplete!
          )}
          {download.completed_at ? (

              {" "}
              (took{" "}
              <DisplayDateDifference
                from={download.created_at}
                to={download.completed_at}
              />
              )

          ) : null}

);

const capitalize = s => {
  return s.charAt(0).toUpperCase() + s.slice(1);
};

export class SortLink extends React.PureComponent {
  change = event => {
    event.preventDefault();
    const { current, name } = this.props;
    let reverse = true;
    if (current && current.sort === name) {
      reverse = !current.reverse;
    }
    this.props.onChangeSort({
      sort: name,
      reverse: reverse
    });
  };
  render() {
    const { current, name } = this.props;
    const title = this.props.title || capitalize(name);
    const currentUrl = document.location.pathname;
    let isCurrent = false;
    let isReverse = true;
    if (current && current.sort === name) {
      isCurrent = true;
      isReverse = current.reverse;
    }
    let arrow;
    if (isCurrent) {
      if (isReverse) {
        arrow = "⬇";
      } else {
        arrow = "⬆";
      }
    } else {
      arrow = "⇣";
    }
    return (
      <Link
        to={currentUrl}
        title={`Click to sort by ${title}`}
        onClick={this.change}
      >
        {arrow}
      </Link>
    );
  }
}
<commit_msg>Remove lint exclusions after eslint update<commit_after>import React from "react";
import FontAwesome from "react-fontawesome";
import "font-awesome/css/font-awesome.css";
import { Link } from "react-router-dom";

import {
  toDate,
  isBefore,
  formatDistance,
  formatDistanceStrict,
  differenceInSeconds,
  differenceInMilliseconds
} from "date-fns";
import parseISO from "date-fns/parseISO";

export function parseISODate(input) {
  return toDate(parseISO(input));
}

export const Loading = () => (

      <FontAwesome name="cog" spin size="5x" />
      Loading...

);

export const DisplayDate = ({ date }) => {
  if (date === null) {
    throw new Error("date is null");
  }
  const dateObj = parseISODate(date);
  const now = new Date();
  if (isBefore(dateObj, now)) {
    return {formatDistance(dateObj, now)} ago;
  } else {
    return in {formatDistance(dateObj, now)};
  }
};

export const DisplayDateDifference = ({ from, to, suffix = "" }) => {
  const fromObj = parseISODate(from);
  const toObj = parseISODate(to);
  const secDiff = differenceInSeconds(toObj, fromObj);
  if (secDiff === 0) {
    const msecDiff = differenceInMilliseconds(toObj, fromObj);
    if (msecDiff > 0) {
      return (
        <span title={`From ${fromObj} to ${toObj}`}>
          {msecDiff} ms
          {suffix && ` ${suffix}`}

      );
    }
  }
  return (
    <span title={`From ${fromObj} to ${toObj}`}>
      {formatDistanceStrict(fromObj, toObj)}
      {suffix && ` ${suffix}`}

  );
};

export const thousandFormat = x => {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};

export const formatFileSize = (bytes, decimals = 0) => {
  if (!bytes) return "0 bytes";
  var k = 1024;
  var dm = decimals + 1 || 3;
  var sizes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  var i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
};

export const formatSeconds = seconds => {
  if (seconds < 1) {
    // milliseconds
    return (seconds * 1000).toFixed(0) + "ms";
  } else if (seconds > 3000) {
    return (seconds / 60).toFixed(1) + "m";
  } else if (seconds < 60) {
    return seconds.toFixed(1) + "s";
  } else {
    const minutes = Math.floor(seconds / 60);
    seconds = Math.round(seconds % 60);
    return `${minutes}m${seconds}s`;
  }
};
export const BooleanIcon = bool => (

);

export const Pagination = ({
  location,
  total,
  batchSize,
  currentPage,
  updateFilter
}) => {
  if (!currentPage) {
    currentPage = 1;
  } else if (typeof currentPage === "string") {
    currentPage = parseInt(currentPage, 10);
  }

  const nextPageUrl = page => {
    const searchParams = new URLSearchParams(location.search);
    searchParams.set("page", page);
    return location.pathname + "?" + searchParams.toString();
  };

  const goTo = (event, page) => {
    event.preventDefault();
    updateFilter({ page });
  };

  const isOverflow = page => {
    // return true if doesn't make sense to go to this page
    return page < 1 || (page - 1) * batchSize >= total;
  };

  return (

      <Link
        className="pagination-previous"
        to={nextPageUrl(currentPage - 1)}
        onClick={e => goTo(e, currentPage - 1)}
        disabled={isOverflow(currentPage - 1)}
      >
        Previous
      </Link>
      <Link
        to={nextPageUrl(currentPage + 1)}
        className="pagination-next"
        onClick={e => goTo(e, currentPage + 1)}
        disabled={isOverflow(currentPage + 1)}
      >
        Next page
      </Link>

  );
};

export const TableSubTitle = ({ total, page, batchSize }) => {
  if (total === null) {
    return null;
  }
  page = page || 1;
  const totalPages = Math.ceil(total / batchSize);
  return (

      {thousandFormat(total)} Found (Page {thousandFormat(page)} of{" "}
      {thousandFormat(totalPages)})

  );
};

export const pluralize = (number, singular, plural) => {
  if (number === 1) {
    return `1 ${singular}`;
  } else {
    return `${number} ${plural}`;
  }
};

export const DisplayFilesSummary = (files, incomplete, skipped, ignored) => {
  const sentences = [];
  sentences.push(pluralize(files, "file uploaded", "files uploaded"));
  if (incomplete) {
    sentences.push(
      pluralize(incomplete, "file incomplete", "files incomplete")
    );
  }
  sentences.push(`${skipped} skipped`);
  // Currently ignoring the 'ignored'
  return sentences.join(". ") + ".";
};

export const ShowValidationErrors = ({ errors, resetAndReload }) => {
  return (

      <button className="delete" onClick={resetAndReload} />

Filter validation errors

        {Object.keys(errors).map(key => {
          return (

              {key} - {errors[key]}

          );
        })}

  );
};

export const parseQueryString = qs => {
  const searchParams = new URLSearchParams(qs);
  const parsed = {};
  for (let [key, value] of searchParams) {
    const already = parsed[key];
    if (already === undefined) {
      parsed[key] = value;
    } else if (Array.isArray(already)) {
      parsed[key].push(value);
    } else {
      parsed[key] = [already, value];
    }
  }
  return parsed;
};

export const filterToQueryString = (filterObj, overrides) => {
  const copy = Object.assign(overrides || {}, filterObj);
  const searchParams = new URLSearchParams();
  Object.entries(copy).forEach(([key, value]) => {
    if (Array.isArray(value) && value.length) {
      value.forEach(v => searchParams.append(key, v));
    } else if (value) {
      searchParams.set(key, value);
    }
  });
  searchParams.sort();
  return searchParams.toString();
};

const URLTag = url => {url};

export const ShowUploadMetadata = ({ upload }) => {
  return (

          User
          {upload.user.email}

          Size
          {formatFileSize(upload.size)}

          Filename
          {upload.filename}

          Try Symbols
          {upload.try_symbols ? "Yes" : "No"}

          Download URL

            {upload.download_url ? URLTag(upload.download_url) : null}

        {upload.download_url && (

            Redirect URLs

              {!upload.redirect_urls.length && n/a}
              {upload.redirect_urls.length ? (

{URLTag(upload.download_url)}

                  {upload.redirect_urls.map(url => (

{URLTag(url)}

                  ))}

              ) : null}

        )}

          Bucket Name
          {upload.bucket_name}

          Bucket Region
          {upload.bucket_region ? upload.bucket_region : null}

          Bucket Endpoint URL

            {upload.bucket_endpoint_url ? (
              upload.bucket_endpoint_url
            ) : (
              null
            )}

          Uploaded

            <DisplayDate date={upload.created_at} />

            Completed

            {upload.completed_at ? (
              <DisplayDate date={upload.completed_at} />
            ) : (

                Incomplete!{" "}
                <DisplayIncompleteRatio files={upload.file_uploads} />

            )}
            {upload.completed_at ? (

                {" "}
                (took{" "}
                <DisplayDateDifference
                  from={upload.created_at}
                  to={upload.completed_at}
                />
                )

            ) : null}

  );
};

const DisplayIncompleteRatio = ({ files }) => {
  // Return a string like "56 of 103 (54%)" to mean that 56 of 103 files are complete.
  const all = files.length;
  const complete = files.filter(file => !!file.completed_at).length;
  const percentage = (100 * complete) / all;
  return `${complete} of ${all} (${Math.trunc(percentage)}%)`;
};

export const ShowFileMetadata = ({ file }) => (

        Key
        {file.key}

        Size
        {formatFileSize(file.size)}

        Bucket Name
        {file.bucket_name}

        Update
        {BooleanIcon(file.update)}

        Compressed
        {BooleanIcon(file.compressed)}

        Uploaded

          <DisplayDate date={file.created_at} />

        Completed

          {file.completed_at ? (
            <DisplayDate date={file.completed_at} />
          ) : (
            Incomplete!
          )}
          {file.completed_at ? (

              {" "}
              (took{" "}
              <DisplayDateDifference
                from={file.created_at}
                to={file.completed_at}
              />
              )

          ) : null}

);

export const ShowMicrosoftDownloadMetadata = ({ download }) => (

        URL
        {download.url}

        Error

          {download.error ? (
            {download.error}
          ) : (
            "-"
          )}

        Created

          <DisplayDate date={download.created_at} />

        Completed

          {download.completed_at ? (
            <DisplayDate date={download.completed_at} />
          ) : (
            Incomplete!
          )}
          {download.completed_at ? (

              {" "}
              (took{" "}
              <DisplayDateDifference
                from={download.created_at}
                to={download.completed_at}
              />
              )

          ) : null}

);

const capitalize = s => {
  return s.charAt(0).toUpperCase() + s.slice(1);
};

export class SortLink extends React.PureComponent {
  change = event => {
    event.preventDefault();
    const { current, name } = this.props;
    let reverse = true;
    if (current && current.sort === name) {
      reverse = !current.reverse;
    }
    this.props.onChangeSort({
      sort: name,
      reverse: reverse
    });
  };
  render() {
    const { current, name } = this.props;
    const title = this.props.title || capitalize(name);
    const currentUrl = document.location.pathname;
    let isCurrent = false;
    let isReverse = true;
    if (current && current.sort === name) {
      isCurrent = true;
      isReverse = current.reverse;
    }
    let arrow;
    if (isCurrent) {
      if (isReverse) {
        arrow = "⬇";
      } else {
        arrow = "⬆";
      }
    } else {
      arrow = "⇣";
    }
    return (
      <Link
        to={currentUrl}
        title={`Click to sort by ${title}`}
        onClick={this.change}
      >
        {arrow}
      </Link>
    );
  }
}
<|endoftext|>