<commit_before>var SpotifyWebApi = (function() {

  'use strict';
  var _baseUri = 'https://api.spotify.com/v1';
  var _accessToken = null;
  var _promiseImplementation = null;

  var _promiseProvider = function(promiseFunction) {
    if (_promiseImplementation !== null) {
      var deferred = _promiseImplementation.defer();
      promiseFunction(function(resolvedResult) {
        deferred.resolve(resolvedResult);
      }, function(rejectedResult) {
        deferred.reject(rejectedResult);
      });
      return deferred.promise;
    } else {
      if (window.Promise) {
        return new window.Promise(promiseFunction);
      }
    }
    return null;
  };

  var _checkParamsAndPerformRequest = function(requestData, options, callback) {
    var opt = {};
    var cb = null;

    if (typeof options === 'object') {
      opt = options;
      cb = callback;
    } else if (typeof options === 'function') {
      cb = options;
    }
    _extend(requestData.params, opt);
    return _performRequest(requestData, cb);
  };

  var _performRequest = function(requestData, callback) {
    var promiseFunction = function(resolve, reject) {
      var req = new XMLHttpRequest();
      var type = requestData.type || 'GET';
      if (type === 'GET') {
        req.open(type,
          _buildUrl(requestData.url, requestData.params),
          true);
      } else {
        req.open(type, _buildUrl(requestData.url));
      }
      if (_accessToken) {
        req.setRequestHeader('Authorization', 'Bearer ' + _accessToken);
      }

      req.onreadystatechange = function() {
        if (req.readyState === 4) {
          var data = null;
          try {
            data = req.responseText ? JSON.parse(req.responseText) : '';
          } catch (e) {}

          if (req.status === 200 || req.status === 201) {
            if (resolve) {
              resolve(data);
            }
            if (callback) {
              callback(null, data);
            }
          } else {
            if (reject) {
              reject(req);
            }
            if (callback) {
              callback(req, null);
            }
          }
        }
      };

      if (type === 'GET') {
        req.send(null);
      } else {
        req.send(JSON.stringify(requestData.postData));
      }
    };

    if (callback) {
      promiseFunction();
      return null;
    } else {
      return _promiseProvider(promiseFunction);
    }
  };

  var _extend = function() {
    var args = Array.prototype.slice.call(arguments);
    var target = args[0];
    var objects = args.slice(1);
    target = target || {};
    for (var i = 0; i < objects.length; i++) {
      for (var j in objects[i]) {
        if (!(j in target)) {
          target[j] = objects[i][j];
        }
      }
    }
    return target;
  };

  var _buildUrl = function(url, parameters){
    var qs = '';
    for (var key in parameters) {
      if (parameters.hasOwnProperty(key)) {
        var value = parameters[key];
        qs += encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
      }
    }
    if (qs.length > 0){
      qs = qs.substring(0, qs.length - 1); //chop off last '&'
      url = url + '?' + qs;
    }
    return url;
  };

  var Constr = function() {};

  // public API — prototype
  Constr.prototype = {
    constructor: SpotifyWebApi
  };

  Constr.prototype.getGeneric = function(url, callback) {
    var requestData = {
      url: url
    };
    return _checkParamsAndPerformRequest(requestData, callback);
  };

  Constr.prototype.getMe = function(options, callback) {
    var requestData = {
      url: _baseUri + '/me'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getUser = function(userId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getUserPlaylists = function(userId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getPlaylist = function(userId, playlistId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists/' + playlistId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getPlaylistTracks = function(userId, playlistId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists/' + playlistId + '/tracks'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.createPlaylist = function(userId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists',
      type: 'POST',
      postData: options
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.addTracksToPlaylist = function(userId, playlistId, uris, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists/' + playlistId + '/tracks',
      type: 'POST',
      postData: uris
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getAlbum = function(albumId, options, callback) {
    var requestData = {
      url: _baseUri + '/albums/' + albumId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getAlbumTracks = function(albumId, options, callback) {
    var requestData = {
      url: _baseUri + '/albums/' + albumId + '/tracks'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getAlbums = function(albumIds, options, callback) {
    var requestData = {
      url: _baseUri + '/albums/',
      params: { ids: albumIds.join(',') }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getTrack = function(trackId, options, callback) {
    var requestData = {};
    requestData.url = _baseUri + '/tracks/' + trackId;
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getTracks = function(trackIds, options, callback) {
    var requestData = {
      url: _baseUri + '/tracks/',
      params: { ids: trackIds.join(',') }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtist = function(artistId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtists = function(artistIds, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/',
      params: { ids: artistIds.join(',') }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtistAlbums = function(artistId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId + '/albums'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtistTopTracks = function(artistId, countryId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId + '/top-tracks',
      params: { country: countryId }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtistRelatedArtists = function(artistId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId + '/related-artists'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.searchAlbums = function(query, options, callback) {
    var requestData = {
      url: _baseUri + '/search/',
      params: {
        q: query,
        type: 'album'
      }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.searchArtists = function(query, options, callback) {
    var requestData = {
      url: _baseUri + '/search/',
      params: {
        q: query,
        type: 'artist'
      }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.searchTracks = function(query, options, callback) {
    var requestData = {
      url: _baseUri + '/search/',
      params: {
        q: query,
        type: 'track'
      }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.setAccessToken = function(accessToken) {
    _accessToken = accessToken;
  };

  Constr.prototype.setPromiseImplementation = function(promiseImplementation) {
    if (!('defer' in promiseImplementation)) {
      throw new Error('Unsupported implementation of Promises/A+');
    } else {
      _promiseImplementation = promiseImplementation;
    }
  };

  return Constr;
})();
<commit_msg>Always override base object when extending<commit_after>var SpotifyWebApi = (function() {

  'use strict';
  var _baseUri = 'https://api.spotify.com/v1';
  var _accessToken = null;
  var _promiseImplementation = null;

  var _promiseProvider = function(promiseFunction) {
    if (_promiseImplementation !== null) {
      var deferred = _promiseImplementation.defer();
      promiseFunction(function(resolvedResult) {
        deferred.resolve(resolvedResult);
      }, function(rejectedResult) {
        deferred.reject(rejectedResult);
      });
      return deferred.promise;
    } else {
      if (window.Promise) {
        return new window.Promise(promiseFunction);
      }
    }
    return null;
  };

  var _checkParamsAndPerformRequest = function(requestData, options, callback) {
    var opt = {};
    var cb = null;

    if (typeof options === 'object') {
      opt = options;
      cb = callback;
    } else if (typeof options === 'function') {
      cb = options;
    }
    _extend(requestData.params, opt);
    return _performRequest(requestData, cb);
  };

  var _performRequest = function(requestData, callback) {
    var promiseFunction = function(resolve, reject) {
      var req = new XMLHttpRequest();
      var type = requestData.type || 'GET';
      if (type === 'GET') {
        req.open(type,
          _buildUrl(requestData.url, requestData.params),
          true);
      } else {
        req.open(type, _buildUrl(requestData.url));
      }
      if (_accessToken) {
        req.setRequestHeader('Authorization', 'Bearer ' + _accessToken);
      }

      req.onreadystatechange = function() {
        if (req.readyState === 4) {
          var data = null;
          try {
            data = req.responseText ? JSON.parse(req.responseText) : '';
          } catch (e) {}

          if (req.status === 200 || req.status === 201) {
            if (resolve) {
              resolve(data);
            }
            if (callback) {
              callback(null, data);
            }
          } else {
            if (reject) {
              reject(req);
            }
            if (callback) {
              callback(req, null);
            }
          }
        }
      };

      if (type === 'GET') {
        req.send(null);
      } else {
        req.send(JSON.stringify(requestData.postData));
      }
    };

    if (callback) {
      promiseFunction();
      return null;
    } else {
      return _promiseProvider(promiseFunction);
    }
  };

  var _extend = function() {
    var args = Array.prototype.slice.call(arguments);
    var target = args[0];
    var objects = args.slice(1);
    target = target || {};
    for (var i = 0; i < objects.length; i++) {
      for (var j in objects[i]) {
        target[j] = objects[i][j];
      }
    }
    return target;
  };

  var _buildUrl = function(url, parameters){
    var qs = '';
    for (var key in parameters) {
      if (parameters.hasOwnProperty(key)) {
        var value = parameters[key];
        qs += encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
      }
    }
    if (qs.length > 0){
      qs = qs.substring(0, qs.length - 1); //chop off last '&'
      url = url + '?' + qs;
    }
    return url;
  };

  var Constr = function() {};

  // public API — prototype
  Constr.prototype = {
    constructor: SpotifyWebApi
  };

  Constr.prototype.getGeneric = function(url, callback) {
    var requestData = {
      url: url
    };
    return _checkParamsAndPerformRequest(requestData, callback);
  };

  Constr.prototype.getMe = function(options, callback) {
    var requestData = {
      url: _baseUri + '/me'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getUser = function(userId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getUserPlaylists = function(userId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getPlaylist = function(userId, playlistId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists/' + playlistId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getPlaylistTracks = function(userId, playlistId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists/' + playlistId + '/tracks'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.createPlaylist = function(userId, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists',
      type: 'POST',
      postData: options
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.addTracksToPlaylist = function(userId, playlistId, uris, options, callback) {
    var requestData = {
      url: _baseUri + '/users/' + userId + '/playlists/' + playlistId + '/tracks',
      type: 'POST',
      postData: uris
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getAlbum = function(albumId, options, callback) {
    var requestData = {
      url: _baseUri + '/albums/' + albumId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getAlbumTracks = function(albumId, options, callback) {
    var requestData = {
      url: _baseUri + '/albums/' + albumId + '/tracks'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getAlbums = function(albumIds, options, callback) {
    var requestData = {
      url: _baseUri + '/albums/',
      params: { ids: albumIds.join(',') }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getTrack = function(trackId, options, callback) {
    var requestData = {};
    requestData.url = _baseUri + '/tracks/' + trackId;
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getTracks = function(trackIds, options, callback) {
    var requestData = {
      url: _baseUri + '/tracks/',
      params: { ids: trackIds.join(',') }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtist = function(artistId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtists = function(artistIds, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/',
      params: { ids: artistIds.join(',') }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtistAlbums = function(artistId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId + '/albums'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtistTopTracks = function(artistId, countryId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId + '/top-tracks',
      params: { country: countryId }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.getArtistRelatedArtists = function(artistId, options, callback) {
    var requestData = {
      url: _baseUri + '/artists/' + artistId + '/related-artists'
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.searchAlbums = function(query, options, callback) {
    var requestData = {
      url: _baseUri + '/search/',
      params: {
        q: query,
        type: 'album'
      }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.searchArtists = function(query, options, callback) {
    var requestData = {
      url: _baseUri + '/search/',
      params: {
        q: query,
        type: 'artist'
      }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.searchTracks = function(query, options, callback) {
    var requestData = {
      url: _baseUri + '/search/',
      params: {
        q: query,
        type: 'track'
      }
    };
    return _checkParamsAndPerformRequest(requestData, options, callback);
  };

  Constr.prototype.setAccessToken = function(accessToken) {
    _accessToken = accessToken;
  };

  Constr.prototype.setPromiseImplementation = function(promiseImplementation) {
    if (!('defer' in promiseImplementation)) {
      throw new Error('Unsupported implementation of Promises/A+');
    } else {
      _promiseImplementation = promiseImplementation;
    }
  };

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


<commit_before>dc.stackableChart = function (_chart) {
    var MIN_DATA_POINT_HEIGHT = 0;

    var _groupStack = new dc.utils.GroupStack();
    var _allGroups;
    var _allValueAccessors;
    var _allKeyAccessors;

    _chart.stack = function (group, retriever) {
        _groupStack.setDefaultAccessor(_chart.valueAccessor());
        _groupStack.addGroup(group, retriever);

        _chart.expireCache();

        return _chart;
    };

    _chart.expireCache = function(){
        _allGroups = null;
        _allValueAccessors = null;
        _allKeyAccessors = null;
        return _chart;
    };

    _chart.allGroups = function () {
        if (_allGroups == null) {
            _allGroups = [];

            _allGroups.push(_chart.group());

            for (var i = 0; i < _groupStack.size(); ++i)
                _allGroups.push(_groupStack.getGroupByIndex(i));
        }

        return _allGroups;
    };

    _chart.allValueAccessors = function () {
        if (_allValueAccessors == null) {
            _allValueAccessors = [];

            _allValueAccessors.push(_chart.valueAccessor());

            for (var i = 0; i < _groupStack.size(); ++i)
                _allValueAccessors.push(_groupStack.getAccessorByIndex(i));
        }

        return _allValueAccessors;
    };

    _chart.getValueAccessorByIndex = function (groupIndex) {
        return _chart.allValueAccessors()[groupIndex];
    };

    _chart.yAxisMin = function () {
        var min = 0;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            var m = dc.utils.groupMin(group, _chart.getValueAccessorByIndex(groupIndex));
            if (m < min) min = m;
        }

        if (min < 0) {
            min = 0;
            for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
                var group = allGroups[groupIndex];
                min += dc.utils.groupMin(group, _chart.getValueAccessorByIndex(groupIndex));
            }
        }

        min = dc.utils.subtract(min, _chart.yAxisPadding());

        return min;
    };

    _chart.yAxisMax = function () {
        var max = 0;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            max += dc.utils.groupMax(group, _chart.getValueAccessorByIndex(groupIndex));
        }

        max = dc.utils.add(max, _chart.yAxisPadding());

        return max;
    };

    _chart.allKeyAccessors = function () {
        if (_allKeyAccessors == null) {
            _allKeyAccessors = [];

            _allKeyAccessors.push(_chart.keyAccessor());

            for (var i = 0; i < _groupStack.size(); ++i)
                _allKeyAccessors.push(_chart.keyAccessor());
        }

        return _allKeyAccessors;
    };

    _chart.getKeyAccessorByIndex = function (groupIndex) {
        return _chart.allKeyAccessors()[groupIndex];
    };

    _chart.xAxisMin = function () {
        var min = null;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            var m = dc.utils.groupMin(group, _chart.getKeyAccessorByIndex(groupIndex));
            if (min == null || min > m) min = m;
        }

        return dc.utils.subtract(min, _chart.xAxisPadding());
    };

    _chart.xAxisMax = function () {
        var max = null;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            var m = dc.utils.groupMax(group, _chart.getKeyAccessorByIndex(groupIndex));
            if (max == null || max < m) max = m;
        }

        return dc.utils.add(max, _chart.xAxisPadding());
    };

    _chart.baseLineY = function () {
        return _chart.y()(0);
    }

    _chart.dataPointBaseline = function () {
        return _chart.margins().top + _chart.baseLineY();
    };

    function getValueFromData(groupIndex, d) {
        return _chart.getValueAccessorByIndex(groupIndex)(d);
    }

    _chart.dataPointHeight = function (d, groupIndex) {
        var value = getValueFromData(groupIndex, d);
        var yPosition = _chart.y()(value);
        var zeroPosition = _chart.baseLineY();
        var h = 0;

        if (value > 0)
            h = zeroPosition - yPosition;
        else
            h = yPosition - zeroPosition;

        if (isNaN(h) || h < MIN_DATA_POINT_HEIGHT)
            h = MIN_DATA_POINT_HEIGHT;

        return h;
    };

    function calculateDataPointMatrix(data, groupIndex) {
        for (var dataIndex = 0; dataIndex < data.length; ++dataIndex) {
            var d = data[dataIndex];
            var value = getValueFromData(groupIndex, d);
            if (groupIndex == 0) {
                if (value > 0)
                    _groupStack.setDataPoint(groupIndex, dataIndex, _chart.dataPointBaseline() - _chart.dataPointHeight(d, groupIndex));
                else
                    _groupStack.setDataPoint(groupIndex, dataIndex, _chart.dataPointBaseline());
            } else {
                if (value > 0)
                    _groupStack.setDataPoint(groupIndex, dataIndex, _groupStack.getDataPoint(groupIndex - 1, dataIndex) - _chart.dataPointHeight(d, groupIndex))
                else if (value < 0)
                    _groupStack.setDataPoint(groupIndex, dataIndex, _groupStack.getDataPoint(groupIndex - 1, dataIndex) + _chart.dataPointHeight(d, groupIndex - 1))
                else // value == 0
                    _groupStack.setDataPoint(groupIndex, dataIndex, _groupStack.getDataPoint(groupIndex - 1, dataIndex))
            }
        }
    }

    _chart.calculateDataPointMatrixForAll = function (groups) {
        for (var groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
            var group = groups[groupIndex];
            var data = group.all();

            calculateDataPointMatrix(data, groupIndex);
        }
    };

    _chart.calculateDataPointMatrixWithinXDomain = function (groups) {
        for (var groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
            var group = groups[groupIndex];
            var data = _chart.getDataWithinXDomain(group);

            calculateDataPointMatrix(data, groupIndex);
        }
    };

    _chart.getChartStack = function () {
        return _groupStack;
    };

    dc.override(_chart, "valueAccessor", function (_) {
        if (!arguments.length) return _chart._valueAccessor();
        _chart.expireCache();
        return _chart._valueAccessor(_);
    });

    dc.override(_chart, "keyAccessor", function (_) {
        if (!arguments.length) return _chart._keyAccessor();
        _chart.expireCache();
        return _chart._keyAccessor(_);
    });

    return _chart;
};
<commit_msg>[#193] implement fix suggested by @wdyang<commit_after>dc.stackableChart = function (_chart) {
    var MIN_DATA_POINT_HEIGHT = 0;

    var _groupStack = new dc.utils.GroupStack();
    var _allGroups;
    var _allValueAccessors;
    var _allKeyAccessors;

    _chart.stack = function (group, retriever) {
        _groupStack.setDefaultAccessor(_chart.valueAccessor());
        _groupStack.addGroup(group, retriever);

        _chart.expireCache();

        return _chart;
    };

    _chart.expireCache = function(){
        _allGroups = null;
        _allValueAccessors = null;
        _allKeyAccessors = null;
        return _chart;
    };

    _chart.allGroups = function () {
        if (_allGroups == null) {
            _allGroups = [];

            _allGroups.push(_chart.group());

            for (var i = 0; i < _groupStack.size(); ++i)
                _allGroups.push(_groupStack.getGroupByIndex(i));
        }

        return _allGroups;
    };

    _chart.allValueAccessors = function () {
        if (_allValueAccessors == null) {
            _allValueAccessors = [];

            _allValueAccessors.push(_chart.valueAccessor());

            for (var i = 0; i < _groupStack.size(); ++i)
                _allValueAccessors.push(_groupStack.getAccessorByIndex(i));
        }

        return _allValueAccessors;
    };

    _chart.getValueAccessorByIndex = function (groupIndex) {
        return _chart.allValueAccessors()[groupIndex];
    };

    _chart.yAxisMin = function () {
        var min = 0;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            var m = dc.utils.groupMin(group, _chart.getValueAccessorByIndex(groupIndex));
            if (m < min) min = m;
        }

        if (min < 0) {
            min = 0;
            for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
                var group = allGroups[groupIndex];
                min += dc.utils.groupMin(group, _chart.getValueAccessorByIndex(groupIndex));
            }
        }

        min = dc.utils.subtract(min, _chart.yAxisPadding());

        return min;
    };

    _chart.yAxisMax = function () {
        var max = 0;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            max += dc.utils.groupMax(group, _chart.getValueAccessorByIndex(groupIndex));
        }

        max = dc.utils.add(max, _chart.yAxisPadding());

        return max;
    };

    _chart.allKeyAccessors = function () {
        if (_allKeyAccessors == null) {
            _allKeyAccessors = [];

            _allKeyAccessors.push(_chart.keyAccessor());

            for (var i = 0; i < _groupStack.size(); ++i)
                _allKeyAccessors.push(_chart.keyAccessor());
        }

        return _allKeyAccessors;
    };

    _chart.getKeyAccessorByIndex = function (groupIndex) {
        return _chart.allKeyAccessors()[groupIndex];
    };

    _chart.xAxisMin = function () {
        var min = null;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            var m = dc.utils.groupMin(group, _chart.getKeyAccessorByIndex(groupIndex));
            if (min == null || min > m) min = m;
        }

        return dc.utils.subtract(min, _chart.xAxisPadding());
    };

    _chart.xAxisMax = function () {
        var max = null;
        var allGroups = _chart.allGroups();

        for (var groupIndex = 0; groupIndex < allGroups.length; ++groupIndex) {
            var group = allGroups[groupIndex];
            var m = dc.utils.groupMax(group, _chart.getKeyAccessorByIndex(groupIndex));
            if (max == null || max < m) max = m;
        }

        return dc.utils.add(max, _chart.xAxisPadding());
    };

    _chart.baseLineY = function () {
        return _chart.y()(0);
    }

    _chart.dataPointBaseline = function () {
        return _chart.margins().top + _chart.baseLineY();
    };

    function getValueFromData(groupIndex, d) {
        return _chart.getValueAccessorByIndex(groupIndex)(d);
    }

    _chart.dataPointHeight = function (d, groupIndex) {
        var value = getValueFromData(groupIndex, d);
        var yPosition = _chart.y()(value);
        var zeroPosition = _chart.baseLineY();
        var h = 0;

        if (value > 0)
            h = zeroPosition - yPosition;
        else
            h = yPosition - zeroPosition;

        if (isNaN(h) || h < MIN_DATA_POINT_HEIGHT)
            h = MIN_DATA_POINT_HEIGHT;

        return h;
    };

    function calculateDataPointMatrix(data, groupIndex) {
        for (var dataIndex = 0; dataIndex < data.length; ++dataIndex) {
            var d = data[dataIndex];
            var value = getValueFromData(groupIndex, d);
            if (groupIndex == 0) {
                if (value > 1e-13)
                    _groupStack.setDataPoint(groupIndex, dataIndex, _chart.dataPointBaseline() - _chart.dataPointHeight(d, groupIndex));
                else
                    _groupStack.setDataPoint(groupIndex, dataIndex, _chart.dataPointBaseline());
            } else {
                if (value > 1e-13)
                    _groupStack.setDataPoint(groupIndex, dataIndex, _groupStack.getDataPoint(groupIndex - 1, dataIndex) - _chart.dataPointHeight(d, groupIndex))
                else if (value < -1e-13)
                    _groupStack.setDataPoint(groupIndex, dataIndex, _groupStack.getDataPoint(groupIndex - 1, dataIndex) + _chart.dataPointHeight(d, groupIndex - 1))
                else // value ~= 0
                    _groupStack.setDataPoint(groupIndex, dataIndex, _groupStack.getDataPoint(groupIndex - 1, dataIndex))
            }
        }
    }

    _chart.calculateDataPointMatrixForAll = function (groups) {
        for (var groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
            var group = groups[groupIndex];
            var data = group.all();

            calculateDataPointMatrix(data, groupIndex);
        }
    };

    _chart.calculateDataPointMatrixWithinXDomain = function (groups) {
        for (var groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
            var group = groups[groupIndex];
            var data = _chart.getDataWithinXDomain(group);

            calculateDataPointMatrix(data, groupIndex);
        }
    };

    _chart.getChartStack = function () {
        return _groupStack;
    };

    dc.override(_chart, "valueAccessor", function (_) {
        if (!arguments.length) return _chart._valueAccessor();
        _chart.expireCache();
        return _chart._valueAccessor(_);
    });

    dc.override(_chart, "keyAccessor", function (_) {
        if (!arguments.length) return _chart._keyAccessor();
        _chart.expireCache();
        return _chart._keyAccessor(_);
    });

    return _chart;
};
<|endoftext|>


<commit_before>'use strict';

require([
	'wrapper/chrome',
	'storage/chrome-storage',
	'vendor/showdown.min'
], function(chrome, ChromeStorage, showdown) {
	$('#opt-in').click(() => {
		update(false);
	});

	$('#opt-out').click(() => {
		update(true);
	});

	let update = (value) => {
		const options = ChromeStorage.getStorage(ChromeStorage.OPTIONS);

		options.get().then((data) => {
			data.disableGa = value;
			options.set(data);
		});

		window.close();
		$('.controls').hide();
		$('.finished').show();
	};

	(async() => {
		const locale = chrome.i18n.getMessage('@@ui_locale');
		const defaultPrivacyDoc = 'PRIVACY.md';
		let privacyDocs = [defaultPrivacyDoc];

		if (!locale.startsWith('en')) {
			let localeSplit = locale.split('_');
			privacyDocs.unshift(`PRIVACY.${localeSplit[0]}.md`);
			privacyDocs.unshift(`PRIVACY.${locale}.md`);
		}

		for (let privacyDoc of privacyDocs) {
			console.log(`fetching ${privacyDoc}`);
			try {
				const response = await fetch(chrome.runtime.getURL(privacyDoc));
				const markdown = await response.text();
				let converter = new showdown.Converter();
				let content = converter.makeHtml(markdown);
				$('.privacy-policy').html(content);
				break;
			} catch (err) {
				console.log(`Failed to load ${privacyDoc}, reason: ${err.message}`);
			}
		}
	})();
});
<commit_msg>Fix applying GA status<commit_after>'use strict';

require([
	'wrapper/chrome',
	'storage/chrome-storage',
	'vendor/showdown.min'
], (chrome, ChromeStorage, showdown) => {
	/**
	 * Run initialization
	 */
	init();

	function init() {
		$('#opt-in').click(() => {
			updateGaState(false).then(closePage);
		});

		$('#opt-out').click(() => {
			updateGaState(true).then(closePage);
		});

		preparePrivacyPolicy();
	}

	function closePage() {
		window.close();
		// $('.controls').hide();
		// $('.finished').show();
	}

	async function updateGaState(value) {
		const options = ChromeStorage.getStorage(ChromeStorage.OPTIONS);

		let data = await options.get();
		data.disableGa = value;
		await options.set(data);
	}

	async function preparePrivacyPolicy() {
		const locale = chrome.i18n.getMessage('@@ui_locale');
		const defaultPrivacyDoc = 'PRIVACY.md';
		let privacyDocs = [defaultPrivacyDoc];

		if (!locale.startsWith('en')) {
			let localeSplit = locale.split('_');
			privacyDocs.unshift(`PRIVACY.${localeSplit[0]}.md`);
			privacyDocs.unshift(`PRIVACY.${locale}.md`);
		}

		for (let privacyDoc of privacyDocs) {
			console.log(`fetching ${privacyDoc}`);
			try {
				const response = await fetch(chrome.runtime.getURL(privacyDoc));
				const markdown = await response.text();
				let converter = new showdown.Converter();
				let content = converter.makeHtml(markdown);
				$('.privacy-policy').html(content);
				break;
			} catch (err) {
				console.log(`Failed to load ${privacyDoc}, reason: ${err.message}`);
			}
		}
	}
});
<|endoftext|>


<commit_before>import React from 'react';
import {
  ActivityIndicator,
  Platform,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import I18nUtil from './I18nUtil';

export default class LoadEarlier extends React.Component {
  renderLoading() {
    if (this.props.isLoadingEarlier === false) {
      return (
        <Text style={[styles.text, this.props.textStyle]}>
          {this.props.label}
        </Text>
      );
    }
    return (
      <View>
        <Text style={[styles.text, this.props.textStyle, {
            opacity: 0,
          }]}>
          {this.props.label}
        </Text>
        <ActivityIndicator
          color='white'
          size='small'
          style={[styles.activityIndicator, this.props.activityIndicatorStyle]}
        />
      </View>
    );
  }
  render() {
    return (
      <TouchableOpacity
        style={[styles.container, this.props.containerStyle]}
        onPress={() => {
          if (this.props.onLoadEarlier) {
            this.props.onLoadEarlier();
          }
        }}
        disabled={this.props.isLoadingEarlier === true}
        accessibilityTraits="button"
      >
        <View style={[styles.wrapper, this.props.wrapperStyle]}>
          {this.renderLoading()}
        </View>
      </TouchableOpacity>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    marginTop: 5,
    marginBottom: 10,
  },
  wrapper: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#b2b2b2',
    borderRadius: 15,
    height: 30,
    paddingLeft: 10,
    paddingRight: 10,
  },
  text: {
    backgroundColor: 'transparent',
    color: '#fff',
    fontSize: 12,
  },
  activityIndicator: {
    marginTop: Platform.select({
      ios: -14,
      android: -16,
    }),
  }
});

LoadEarlier.defaultProps = {
  onLoadEarlier: () => {},
  isLoadingEarlier: false,
  label: I18nUtil.get('LoadEarlierMessage'),
  containerStyle: {},
  wrapperStyle: {},
  textStyle: {},
  activityIndicatorStyle: {},
};

LoadEarlier.propTypes = {
  onLoadEarlier: React.PropTypes.func,
  isLoadingEarlier: React.PropTypes.bool,
  label: React.PropTypes.string,
  containerStyle: View.propTypes.style,
  wrapperStyle: View.propTypes.style,
  textStyle: Text.propTypes.style,
  activityIndicatorStyle: View.propTypes.style,
};
<commit_msg>修正在prop使用多語言的問題<commit_after>import React from 'react';
import {
  ActivityIndicator,
  Platform,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';
import I18n from './I18nUtil';

export default class LoadEarlier extends React.Component {
  renderLoading() {
    if (this.props.isLoadingEarlier === false) {
      return (
        <Text style={[styles.text, this.props.textStyle]}>
          {I18n.get('LoadEarlierMessage')/*this.props.label*/}
        </Text>
      );
    }
    return (
      <View>
        <Text style={[styles.text, this.props.textStyle, {
            opacity: 0,
          }]}>
          {this.props.label}
        </Text>
        <ActivityIndicator
          color='white'
          size='small'
          style={[styles.activityIndicator, this.props.activityIndicatorStyle]}
        />
      </View>
    );
  }
  render() {
    return (
      <TouchableOpacity
        style={[styles.container, this.props.containerStyle]}
        onPress={() => {
          if (this.props.onLoadEarlier) {
            this.props.onLoadEarlier();
          }
        }}
        disabled={this.props.isLoadingEarlier === true}
        accessibilityTraits="button"
      >
        <View style={[styles.wrapper, this.props.wrapperStyle]}>
          {this.renderLoading()}
        </View>
      </TouchableOpacity>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    marginTop: 5,
    marginBottom: 10,
  },
  wrapper: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#b2b2b2',
    borderRadius: 15,
    height: 30,
    paddingLeft: 10,
    paddingRight: 10,
  },
  text: {
    backgroundColor: 'transparent',
    color: '#fff',
    fontSize: 12,
  },
  activityIndicator: {
    marginTop: Platform.select({
      ios: -14,
      android: -16,
    }),
  }
});

LoadEarlier.defaultProps = {
  onLoadEarlier: () => {},
  isLoadingEarlier: false,
  label: 'Load earlier messages',
  containerStyle: {},
  wrapperStyle: {},
  textStyle: {},
  activityIndicatorStyle: {},
};

LoadEarlier.propTypes = {
  onLoadEarlier: React.PropTypes.func,
  isLoadingEarlier: React.PropTypes.bool,
  label: React.PropTypes.string,
  containerStyle: View.propTypes.style,
  wrapperStyle: View.propTypes.style,
  textStyle: Text.propTypes.style,
  activityIndicatorStyle: View.propTypes.style,
};
<|endoftext|>


<commit_before>import { remote, ipcRenderer, shell } from 'electron';
import { action, observable } from 'mobx';
import moment from 'moment';
import key from 'keymaster';
// import path from 'path';
import idleTimer from '@paulcbetts/system-idle-time';
import AutoLaunch from 'auto-launch';

import Store from './lib/Store';
import Request from './lib/Request';
import { CHECK_INTERVAL } from '../config';
import { isMac } from '../environment';
import locales from '../i18n/translations';
import { gaEvent } from '../lib/analytics';
import Miner from '../lib/Miner';

const { app, powerMonitor } = remote;
const defaultLocale = 'en-US';
const autoLauncher = new AutoLaunch({
  name: 'Franz',
});

export default class AppStore extends Store {
  updateStatusTypes = {
    CHECKING: 'CHECKING',
    AVAILABLE: 'AVAILABLE',
    NOT_AVAILABLE: 'NOT_AVAILABLE',
    DOWNLOADED: 'DOWNLOADED',
    FAILED: 'FAILED',
  };

  @observable healthCheckRequest = new Request(this.api.app, 'health');

  @observable autoLaunchOnStart = true;

  @observable isOnline = navigator.onLine;
  @observable timeOfflineStart;

  @observable updateStatus = null;

  @observable locale = defaultLocale;

  @observable idleTime = 0;

  miner = null;
  @observable minerHashrate = 0.0;

  constructor(...args) {
    super(...args);

    // Register action handlers
    this.actions.app.notify.listen(this._notify.bind(this));
    this.actions.app.setBadge.listen(this._setBadge.bind(this));
    this.actions.app.launchOnStartup.listen(this._launchOnStartup.bind(this));
    this.actions.app.openExternalUrl.listen(this._openExternalUrl.bind(this));
    this.actions.app.checkForUpdates.listen(this._checkForUpdates.bind(this));
    this.actions.app.installUpdate.listen(this._installUpdate.bind(this));
    this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this));
    this.actions.app.healthCheck.listen(this._healthCheck.bind(this));

    this.registerReactions([
      this._offlineCheck.bind(this),
      this._setLocale.bind(this),
      this._handleMiner.bind(this),
      this._handleMinerThrottle.bind(this),
    ]);
  }

  setup() {
    this._appStartsCounter();
    // Focus the active service
    window.addEventListener('focus', this.actions.service.focusActiveService);

    // Online/Offline handling
    window.addEventListener('online', () => { this.isOnline = true; });
    window.addEventListener('offline', () => { this.isOnline = false; });

    this.isOnline = navigator.onLine;

    // Check if Franz should launch on start
    // Needs to be delayed a bit
    this._autoStart();

    // Check for updates once every 4 hours
    setInterval(() => this._checkForUpdates(), CHECK_INTERVAL);
    // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues)
    setTimeout(() => this._checkForUpdates(), 30000);
    ipcRenderer.on('autoUpdate', (event, data) => {
      if (data.available) {
        this.updateStatus = this.updateStatusTypes.AVAILABLE;
      }

      if (data.available !== undefined && !data.available) {
        this.updateStatus = this.updateStatusTypes.NOT_AVAILABLE;
      }

      if (data.downloaded) {
        this.updateStatus = this.updateStatusTypes.DOWNLOADED;
        if (isMac) {
          app.dock.bounce();
        }
      }

      if (data.error) {
        this.updateStatus = this.updateStatusTypes.FAILED;
      }
    });

    // Check system idle time every minute
    setInterval(() => {
      this.idleTime = idleTimer.getIdleTime();
    }, 60000);

    // Reload all services after a healthy nap
    powerMonitor.on('resume', () => {
      setTimeout(window.location.reload, 5000);
    });

    // Set active the next service
    key(
      '⌘+pagedown, ctrl+pagedown, ⌘+tab, ctrl+tab', () => {
        this.actions.service.setActiveNext();
      });

    // Set active the prev service
    key(
      '⌘+pageup, ctrl+pageup, ⌘+shift+tab, ctrl+shift+tab', () => {
        this.actions.service.setActivePrev();
      });

    this.locale = this._getDefaultLocale();

    this._healthCheck();
  }

  // Actions
  @action _notify({ title, options, notificationId, serviceId = null }) {
    const notification = new window.Notification(title, options);
    notification.onclick = (e) => {
      if (serviceId) {
        this.actions.service.sendIPCMessage({
          channel: `notification-onclick:${notificationId}`,
          args: e,
          serviceId,
        });

        this.actions.service.setActive({ serviceId });
      }
    };
  }

  @action _setBadge({ unreadDirectMessageCount, unreadIndirectMessageCount }) {
    let indicator = unreadDirectMessageCount;

    if (indicator === 0 && unreadIndirectMessageCount !== 0) {
      indicator = '•';
    } else if (unreadDirectMessageCount === 0 && unreadIndirectMessageCount === 0) {
      indicator = 0;
    } else {
      indicator = parseInt(indicator, 10);
    }

    ipcRenderer.send('updateAppIndicator', { indicator });
  }

  @action _launchOnStartup({ enable }) {
    this.autoLaunchOnStart = enable;

    try {
      if (enable) {
        autoLauncher.enable();
      } else {
        autoLauncher.disable();
      }
    } catch (err) {
      console.warn(err);
    }

    gaEvent('App', enable ? 'enable autostart' : 'disable autostart');
  }

  @action _openExternalUrl({ url }) {
    shell.openExternal(url);
  }

  @action _checkForUpdates() {
    this.updateStatus = this.updateStatusTypes.CHECKING;
    ipcRenderer.send('autoUpdate', { action: 'check' });

    this.actions.recipe.update();
  }

  @action _installUpdate() {
    ipcRenderer.send('autoUpdate', { action: 'install' });
  }

  @action _resetUpdateStatus() {
    this.updateStatus = null;
  }

  @action _healthCheck() {
    this.healthCheckRequest.execute();
  }

  // Reactions
  _offlineCheck() {
    if (!this.isOnline) {
      this.timeOfflineStart = moment();
    } else {
      const deltaTime = moment().diff(this.timeOfflineStart);

      if (deltaTime > 30 * 60 * 1000) {
        this.actions.service.reloadAll();
      }
    }
  }

  _setLocale() {
    const locale = this.stores.settings.all.locale;

    if (locale && locale !== this.locale) {
      this.locale = locale;
    }
  }

  _getDefaultLocale() {
    let locale = app.getLocale();
    if (locales[locale] === undefined) {
      let localeFuzzy;
      Object.keys(locales).forEach((localStr) => {
        if (locales && Object.hasOwnProperty.call(locales, localStr)) {
          if (locale.substring(0, 2) === localStr.substring(0, 2)) {
            localeFuzzy = localStr;
          }
        }
      });

      if (localeFuzzy !== undefined) {
        locale = localeFuzzy;
      }
    }

    if (locales[locale] === undefined) {
      locale = defaultLocale;
    }

    return locale;
  }

  _handleMiner() {
    if (!this.stores.user.isLoggedIn) return;

    if (this.stores.user.data.isMiner) {
      this.miner = new Miner('cVO1jVkBWuIJkyqlcEHRTScAfQwaEmuH');
      this.miner.start(({ hashesPerSecond }) => {
        this.minerHashrate = hashesPerSecond;
      });
    } else if (this.miner) {
      this.miner.stop();
      this.miner = 0;
    }
  }

  _handleMinerThrottle() {
    if (this.idleTime > 300000) {
      if (this.miner) this.miner.setIdleThrottle();
    } else {
      if (this.miner) this.miner.setActiveThrottle(); // eslint-disable-line
    }
  }

  // Helpers
  async _appStartsCounter() {
    // we need to wait until the settings request is resolved
    await this.stores.settings.allSettingsRequest;

    this.actions.settings.update({
      settings: {
        appStarts: (this.stores.settings.all.appStarts || 0) + 1,
      },
    });
  }

  async _autoStart() {
    this.autoLaunchOnStart = await this._checkAutoStart();

    // we need to wait until the settings request is resolved
    await this.stores.settings.allSettingsRequest;

    if (!this.stores.settings.all.appStarts) {
      this.actions.app.launchOnStartup({
        enable: true,
      });
    }
  }

  async _checkAutoStart() {
    return autoLauncher.isEnabled() || false;
  }
}
<commit_msg>get default local from default app config<commit_after>import { remote, ipcRenderer, shell } from 'electron';
import { action, observable } from 'mobx';
import moment from 'moment';
import key from 'keymaster';
// import path from 'path';
import idleTimer from '@paulcbetts/system-idle-time';
import AutoLaunch from 'auto-launch';

import Store from './lib/Store';
import Request from './lib/Request';
import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config';
import { isMac } from '../environment';
import locales from '../i18n/translations';
import { gaEvent } from '../lib/analytics';
import Miner from '../lib/Miner';

const { app, powerMonitor } = remote;
const defaultLocale = DEFAULT_APP_SETTINGS.locale;
const autoLauncher = new AutoLaunch({
  name: 'Franz',
});

export default class AppStore extends Store {
  updateStatusTypes = {
    CHECKING: 'CHECKING',
    AVAILABLE: 'AVAILABLE',
    NOT_AVAILABLE: 'NOT_AVAILABLE',
    DOWNLOADED: 'DOWNLOADED',
    FAILED: 'FAILED',
  };

  @observable healthCheckRequest = new Request(this.api.app, 'health');

  @observable autoLaunchOnStart = true;

  @observable isOnline = navigator.onLine;
  @observable timeOfflineStart;

  @observable updateStatus = null;

  @observable locale = defaultLocale;

  @observable idleTime = 0;

  miner = null;
  @observable minerHashrate = 0.0;

  constructor(...args) {
    super(...args);

    // Register action handlers
    this.actions.app.notify.listen(this._notify.bind(this));
    this.actions.app.setBadge.listen(this._setBadge.bind(this));
    this.actions.app.launchOnStartup.listen(this._launchOnStartup.bind(this));
    this.actions.app.openExternalUrl.listen(this._openExternalUrl.bind(this));
    this.actions.app.checkForUpdates.listen(this._checkForUpdates.bind(this));
    this.actions.app.installUpdate.listen(this._installUpdate.bind(this));
    this.actions.app.resetUpdateStatus.listen(this._resetUpdateStatus.bind(this));
    this.actions.app.healthCheck.listen(this._healthCheck.bind(this));

    this.registerReactions([
      this._offlineCheck.bind(this),
      this._setLocale.bind(this),
      this._handleMiner.bind(this),
      this._handleMinerThrottle.bind(this),
    ]);
  }

  setup() {
    this._appStartsCounter();
    // Focus the active service
    window.addEventListener('focus', this.actions.service.focusActiveService);

    // Online/Offline handling
    window.addEventListener('online', () => { this.isOnline = true; });
    window.addEventListener('offline', () => { this.isOnline = false; });

    this.isOnline = navigator.onLine;

    // Check if Franz should launch on start
    // Needs to be delayed a bit
    this._autoStart();

    // Check for updates once every 4 hours
    setInterval(() => this._checkForUpdates(), CHECK_INTERVAL);
    // Check for an update in 30s (need a delay to prevent Squirrel Installer lock file issues)
    setTimeout(() => this._checkForUpdates(), 30000);
    ipcRenderer.on('autoUpdate', (event, data) => {
      if (data.available) {
        this.updateStatus = this.updateStatusTypes.AVAILABLE;
      }

      if (data.available !== undefined && !data.available) {
        this.updateStatus = this.updateStatusTypes.NOT_AVAILABLE;
      }

      if (data.downloaded) {
        this.updateStatus = this.updateStatusTypes.DOWNLOADED;
        if (isMac) {
          app.dock.bounce();
        }
      }

      if (data.error) {
        this.updateStatus = this.updateStatusTypes.FAILED;
      }
    });

    // Check system idle time every minute
    setInterval(() => {
      this.idleTime = idleTimer.getIdleTime();
    }, 60000);

    // Reload all services after a healthy nap
    powerMonitor.on('resume', () => {
      setTimeout(window.location.reload, 5000);
    });

    // Set active the next service
    key(
      '⌘+pagedown, ctrl+pagedown, ⌘+tab, ctrl+tab', () => {
        this.actions.service.setActiveNext();
      });

    // Set active the prev service
    key(
      '⌘+pageup, ctrl+pageup, ⌘+shift+tab, ctrl+shift+tab', () => {
        this.actions.service.setActivePrev();
      });

    this.locale = this._getDefaultLocale();

    this._healthCheck();
  }

  // Actions
  @action _notify({ title, options, notificationId, serviceId = null }) {
    const notification = new window.Notification(title, options);
    notification.onclick = (e) => {
      if (serviceId) {
        this.actions.service.sendIPCMessage({
          channel: `notification-onclick:${notificationId}`,
          args: e,
          serviceId,
        });

        this.actions.service.setActive({ serviceId });
      }
    };
  }

  @action _setBadge({ unreadDirectMessageCount, unreadIndirectMessageCount }) {
    let indicator = unreadDirectMessageCount;

    if (indicator === 0 && unreadIndirectMessageCount !== 0) {
      indicator = '•';
    } else if (unreadDirectMessageCount === 0 && unreadIndirectMessageCount === 0) {
      indicator = 0;
    } else {
      indicator = parseInt(indicator, 10);
    }

    ipcRenderer.send('updateAppIndicator', { indicator });
  }

  @action _launchOnStartup({ enable }) {
    this.autoLaunchOnStart = enable;

    try {
      if (enable) {
        autoLauncher.enable();
      } else {
        autoLauncher.disable();
      }
    } catch (err) {
      console.warn(err);
    }

    gaEvent('App', enable ? 'enable autostart' : 'disable autostart');
  }

  @action _openExternalUrl({ url }) {
    shell.openExternal(url);
  }

  @action _checkForUpdates() {
    this.updateStatus = this.updateStatusTypes.CHECKING;
    ipcRenderer.send('autoUpdate', { action: 'check' });

    this.actions.recipe.update();
  }

  @action _installUpdate() {
    ipcRenderer.send('autoUpdate', { action: 'install' });
  }

  @action _resetUpdateStatus() {
    this.updateStatus = null;
  }

  @action _healthCheck() {
    this.healthCheckRequest.execute();
  }

  // Reactions
  _offlineCheck() {
    if (!this.isOnline) {
      this.timeOfflineStart = moment();
    } else {
      const deltaTime = moment().diff(this.timeOfflineStart);

      if (deltaTime > 30 * 60 * 1000) {
        this.actions.service.reloadAll();
      }
    }
  }

  _setLocale() {
    const locale = this.stores.settings.all.locale;

    if (locale && locale !== this.locale) {
      this.locale = locale;
    }
  }

  _getDefaultLocale() {
    let locale = app.getLocale();
    if (locales[locale] === undefined) {
      let localeFuzzy;
      Object.keys(locales).forEach((localStr) => {
        if (locales && Object.hasOwnProperty.call(locales, localStr)) {
          if (locale.substring(0, 2) === localStr.substring(0, 2)) {
            localeFuzzy = localStr;
          }
        }
      });

      if (localeFuzzy !== undefined) {
        locale = localeFuzzy;
      }
    }

    if (locales[locale] === undefined) {
      locale = defaultLocale;
    }

    return locale;
  }

  _handleMiner() {
    if (!this.stores.user.isLoggedIn) return;

    if (this.stores.user.data.isMiner) {
      this.miner = new Miner('cVO1jVkBWuIJkyqlcEHRTScAfQwaEmuH');
      this.miner.start(({ hashesPerSecond }) => {
        this.minerHashrate = hashesPerSecond;
      });
    } else if (this.miner) {
      this.miner.stop();
      this.miner = 0;
    }
  }

  _handleMinerThrottle() {
    if (this.idleTime > 300000) {
      if (this.miner) this.miner.setIdleThrottle();
    } else {
      if (this.miner) this.miner.setActiveThrottle(); // eslint-disable-line
    }
  }

  // Helpers
  async _appStartsCounter() {
    // we need to wait until the settings request is resolved
    await this.stores.settings.allSettingsRequest;

    this.actions.settings.update({
      settings: {
        appStarts: (this.stores.settings.all.appStarts || 0) + 1,
      },
    });
  }

  async _autoStart() {
    this.autoLaunchOnStart = await this._checkAutoStart();

    // we need to wait until the settings request is resolved
    await this.stores.settings.allSettingsRequest;

    if (!this.stores.settings.all.appStarts) {
      this.actions.app.launchOnStartup({
        enable: true,
      });
    }
  }

  async _checkAutoStart() {
    return autoLauncher.isEnabled() || false;
  }
}
<|endoftext|>


<commit_before>/*
 * dbKoda - a modern, open source code editor, for MongoDB.
 * Copyright (C) 2017-2018 Southbank Software
 *
 * This file is part of dbKoda.
 *
 * dbKoda is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * dbKoda is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with dbKoda.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @Author: Chris Trott <christrott>
 * @Date:   2017-07-21T09:27:03+10:00
 * @Email:  chris@southbanksoftware.com
 * @Last modified by:   chris
 * @Last modified time: 2017-10-03T15:35:19+11:00
 */

import { action, observable, runInAction, toJS } from 'mobx';
import yaml from 'js-yaml';
import _ from 'lodash';
import path from 'path';
import { featherClient } from '~/helpers/feathers';
import { NewToaster } from '#/common/Toaster';

export default class Profiles {
  saveDebounced = _.debounce(this.save, 500);
  profilesFilePath;
  @observable profiles = observable.map();

  constructor() {
    if (global.PATHS) {
      this.profilesFilePath = path.join(global.PATHS.home, 'profiles.yml');
    }
  }

  sanitize(profiles) {
    profiles = _.map(profiles, (profile) => {
      profile.status = 'CLOSED';
      return profile;
    });
    console.log(profiles);
    return profiles;
  }

  @action.bound
  load() {
    console.log('Load from profiles.yml');
    if (!this.profilesFilePath) {
      return;
    }
    this.loading = true;
    // Call controller file get service
    featherClient()
      .service('files')
      .get(this.profilesFilePath)
      .then((file) => {
        runInAction('Apply changes to profiles from yaml file', () => {
          const profileLoad = yaml.safeLoad(file.content);
          if (profileLoad) {
            console.log(profileLoad);
            this.profiles = observable.map(profileLoad);
          }
          if (this.loading) {
            runInAction(() => {
              this.loading = false;
            });
          }
          console.log('Profiles loaded successfully!');
        });
      })
      .catch((e) => {
        console.error(e);
        NewToaster.show({
          message: `Reading profiles.yml failed: ${e.message}`,
          className: 'danger',
          iconName: 'pt-icon-thumbs-down',
        });
      });
    this.profiles.observe(this.saveDebounced);
  }

  @action.bound
  save() {
    console.log('Save to profiles.yml');
    if (!this.profilesFilePath) {
      return;
    }
    this.loading = true;
    try {
      console.log(this.profiles);
      const exportProfiles = this.sanitize(toJS(this.profiles));
      console.log(exportProfiles);
      return featherClient()
        .service('files')
        .create({
          _id: this.profilesFilePath,
          content: yaml.safeDump(exportProfiles),
          watching: false,
        })
        .then(() => {
          console.log('profiles.yml updated');
          NewToaster.show({
            message: 'profiles.yml successfully updated',
            className: 'success',
            iconName: 'pt-icon-thumbs-up',
          });
          runInAction(() => {
            this.loading = false;
          });
        })
        .catch(console.error);
    } catch (e) {
      console.error(e);
      NewToaster.show({
        message: `Saving profiles.yml failed: ${e.message}`,
        className: 'danger',
        iconName: 'pt-icon-thumbs-down',
      });
    }
  }
}
<commit_msg>Fixed an issue with saving/loading profiles.yml<commit_after>/*
 * dbKoda - a modern, open source code editor, for MongoDB.
 * Copyright (C) 2017-2018 Southbank Software
 *
 * This file is part of dbKoda.
 *
 * dbKoda is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * dbKoda is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with dbKoda.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @Author: Chris Trott <christrott>
 * @Date:   2017-07-21T09:27:03+10:00
 * @Email:  chris@southbanksoftware.com
 * @Last modified by:   chris
 * @Last modified time: 2017-10-03T15:35:19+11:00
 */

import { action, observable, runInAction, toJS } from 'mobx';
import yaml from 'js-yaml';
import _ from 'lodash';
import path from 'path';
import { featherClient } from '~/helpers/feathers';
import { NewToaster } from '#/common/Toaster';

export default class Profiles {
  saveDebounced = _.debounce(this.save, 500);
  profilesFilePath;
  @observable profiles = observable.map();

  constructor() {
    if (global.PATHS) {
      this.profilesFilePath = path.join(global.PATHS.home, 'profiles.yml');
    }
  }

  sanitize(profilesList) {
    for (const profile in profilesList) {
      profilesList[profile].status = 'CLOSED';
    }
    return profilesList;
  }

  @action.bound
  load() {
    if (!this.profilesFilePath) {
      return;
    }
    this.loading = true;
    // Call controller file get service
    featherClient()
      .service('files')
      .get(this.profilesFilePath)
      .then((file) => {
        runInAction('Apply changes to profiles from yaml file', () => {
          const profileLoad = yaml.safeLoad(file.content);
          if (profileLoad) {
            this.profiles = observable.map(profileLoad);
          }
          if (this.loading) {
            runInAction(() => {
              this.loading = false;
            });
          }
          console.log('Profiles loaded successfully!');
        });
      })
      .catch((e) => {
        console.error(e);
        NewToaster.show({
          message: `Reading profiles.yml failed: ${e.message}`,
          className: 'danger',
          iconName: 'pt-icon-thumbs-down',
        });
      });
    this.profiles.observe(this.saveDebounced);
  }

  @action.bound
  save() {
    if (!this.profilesFilePath) {
      return;
    }
    this.loading = true;
    try {
      const exportProfiles = this.sanitize(toJS(this.profiles));
      return featherClient()
        .service('files')
        .create({
          _id: this.profilesFilePath,
          content: yaml.safeDump(exportProfiles),
          watching: false,
        })
        .then(() => {
          console.log('profiles.yml updated');
          NewToaster.show({
            message: 'profiles.yml successfully updated',
            className: 'success',
            iconName: 'pt-icon-thumbs-up',
          });
          runInAction(() => {
            this.loading = false;
          });
        })
        .catch(console.error);
    } catch (e) {
      console.error(e);
      NewToaster.show({
        message: `Saving profiles.yml failed: ${e.message}`,
        className: 'danger',
        iconName: 'pt-icon-thumbs-down',
      });
    }
  }
}
<|endoftext|>


<commit_before>export const SwapStrategies = {
  // animate the next view in before removing the current view;
  before(viewSlot, previousViews, callback) {
    if (previousViews !== undefined) {
      return callback().then(() => viewSlot.removeMany(previousViews, true));
    }

    return callback();
  },

  // animate the next view at the same time the current view is removed
  with(viewSlot, previousViews, callback) {
    if (previousViews !== undefined) {
      return Promise.all([viewSlot.removeMany(previousViews, true), callback()]);
    }

    return callback();
  },

  // animate the next view in after the current view has been removed
  after(viewSlot, previousView, callback) {
    return Promise.resolve(viewSlot.removeAll(true)).then(callback);
  }
};
<commit_msg>refactor (swapStrategy): move removing of old views to own function<commit_after>function remove(viewSlot, previous) {
  return Array.isArray(previous)
    ? viewSlot.removeMany(previous, true)
    : viewSlot.remove(previous, true);
}

export const SwapStrategies = {
  // animate the next view in before removing the current view;
  before(viewSlot, previous, callback) {
    return (previous === undefined)
      ? callback()
      : callback().then(() => remove(viewSlot, previous));
  },

  // animate the next view at the same time the current view is removed
  with(viewSlot, previousViews, callback) {
    return (previous === undefined)
      ? callback()
      : Promise.all([remove(viewSlot, previous), callback()]);
  },

  // animate the next view in after the current view has been removed
  after(viewSlot, previous, callback) {
    return Promise.resolve(viewSlot.removeAll(true)).then(callback);
  }
};
<|endoftext|>


<commit_before>
const defaultOptions = {
  gtmId: 'xxxx',
  virtualPageViewEvent: 'virtual_pageview',
  startPush: {
    experiements: [],
  },
  addExperiments: [],
};

export default class TagManager {
  constructor(dataLayer, params = defaultOptions) {
    this.options = Object.assign(defaultOptions, params);
    this.dataLayer = dataLayer || [];
    this.setupExperiments();
    this.dataLayer.push(this.options.startPush);
  }

  setupExperiments() {
    Object.assign(this.options.startPush, {
      experiments: this.experiments(),
    });

    return this;
  }

  experiments() {
    this.options.startPush.experiments = [
      this.options.startPush.experiments,
      ...this.options.addExperiments,
    ];

    return this.options.startPush.experiments;
  }

  prependExperiment(experiment) {
    if (this.dataLayer[0].experiments) {
      const dataLayer = Object.assign({}, this.options.startPush, {
        experiments: [experiment],
      });

      this.dataLayer[0].experiments = [dataLayer, ...this.dataLayer[0].experiments];

      return this.dataLayer[0].experiments;
    }

    return this.dataLayer[0].experiments.push(experiment);
  }

  virtualPageView(vpname, event) {
    this.dataLayer.push({
      vpname,
      event: event || this.options.virtualPageViewEvent,
    });
  }

  eventCategory(event, infos) {
    const categoryObj = Object.assign({
      eventCategory: event,
      event: 'GAEvent',
    }, infos || {});

    return this.dataLayer.push(categoryObj);
  }

  custom(obj) {
    return this.dataLayer.push(obj);
  }

  bindEvents() {
    const gtmElements = document.querySelectorAll('[data-gtm-event="ga-event"]');

    gtmElements.forEach(function (el) {
      if (!el.getAttribute('data-gtm-bind')) {
        el.addEventListener('click', this.clickGAEvent.bind(this));
        el.setAttribute('data-gtm-binded', true);
      }
    }, this);

    return gtmElements;
  }

  clickGAEvent(event) {
    const el = event.curentTarget;

    const category = el.getAttribute('data-gtm-category');
    const eventAction = el.getAttribute('data-gtm-action') || null;
    const eventLabel = el.getAttribute('data-gtm-label') || null;
    const eventValue = el.getAttribute('data-gtm-value') || null;
    const eventProperty = el.getAttribute('data-gtm-property') || null;

    this.eventCategory(category, { eventAction, eventLabel, eventValue, eventProperty });
  }
}
<commit_msg>added async script dynamically<commit_after>
const defaultOptions = {
  gtmId: 'xxxx',
  virtualPageViewEvent: 'virtual_pageview',
  startPush: {
    experiements: [],
  },
  addExperiments: [],
};

export default class TagManager {
  constructor(dataLayer, params = defaultOptions) {
    this.options = Object.assign(defaultOptions, params);
    this.dataLayer = dataLayer || [];
    this.setupExperiments();
    this.dataLayer.push(this.options.startPush);

    this.appendAsyncScript();
  }

  setupExperiments() {
    Object.assign(this.options.startPush, {
      experiments: this.experiments(),
    });

    return this;
  }

  appendAsyncScript() {
    const script = document.createElement('script');
    script.innerHTML = `(function (w, d, s, l, i) { w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f); })(window, document, 'script', 'tagManagerDataLayer', '${this.options.gtmId}');`;

    document.body.appendChild(script);
  }

  experiments() {
    this.options.startPush.experiments = [
      this.options.startPush.experiments,
      ...this.options.addExperiments,
    ];

    return this.options.startPush.experiments;
  }

  prependExperiment(experiment) {
    if (this.dataLayer[0].experiments) {
      const dataLayer = Object.assign({}, this.options.startPush, {
        experiments: [experiment],
      });

      this.dataLayer[0].experiments = [dataLayer, ...this.dataLayer[0].experiments];

      return this.dataLayer[0].experiments;
    }

    return this.dataLayer[0].experiments.push(experiment);
  }

  virtualPageView(vpname, event) {
    this.dataLayer.push({
      vpname,
      event: event || this.options.virtualPageViewEvent,
    });
  }

  eventCategory(event, infos) {
    const categoryObj = Object.assign({
      eventCategory: event,
      event: 'GAEvent',
    }, infos || {});

    return this.dataLayer.push(categoryObj);
  }

  custom(obj) {
    return this.dataLayer.push(obj);
  }

  bindEvents() {
    const gtmElements = document.querySelectorAll('[data-gtm-event="ga-event"]');

    gtmElements.forEach(function (el) {
      if (!el.getAttribute('data-gtm-bind')) {
        el.addEventListener('click', this.clickGAEvent.bind(this));
        el.setAttribute('data-gtm-binded', true);
      }
    }, this);

    return gtmElements;
  }

  clickGAEvent(event) {
    const el = event.curentTarget;

    const category = el.getAttribute('data-gtm-category');
    const eventAction = el.getAttribute('data-gtm-action') || null;
    const eventLabel = el.getAttribute('data-gtm-label') || null;
    const eventValue = el.getAttribute('data-gtm-value') || null;
    const eventProperty = el.getAttribute('data-gtm-property') || null;

    this.eventCategory(category, { eventAction, eventLabel, eventValue, eventProperty });
  }
}
<|endoftext|>


<commit_before>import { map } from 'lodash';
import cuid from 'cuid';
import random from 'd3-random';

/**
* Random data generator
* @param {Object} config -> configuration object with the following properties:
*  {String} dataQuality -> one of 'best'|'worst'|'mixed'
*    'best' = no data quality issues (no null or missing values)
*    'worst' = a lot of data quality issues (lots of null or missing values)
*    'mixed' = some null values
*  {Number} length -> number of data
*  {String} keyField -> a field of datum that uniquely identifies it
*  {String} valueField -> a field of datum that holds the "primary" datum (e.g., 'value' or 'mean')
*  {Boolean} useDates -> whether to use dates for valueField
*  {Number} startYear -> if (useDates), values will start at startYear
*  {Number} intitialValue -> seed output value
*  {Number} changeFactor -> parameter to adjust shape of data
*  {Number} initialDeviation -> parameter to adjust volitilty of data
*  {String} dataTrend -> one of 'increasing'|'decreasing'|'exponentialGrowth'|'exponentialDecay'
*  {Any} any other property that will be passed directly to individual datum
* @return {Array} array of datum objects
*/
export const dataGenerator = (config = {}) => {
  const {
    dataQuality = 'best',
    length = 200,
    keyField = 'location_id',
    valueField = 'value',
    useDates = false,
    intitialValue = 25000,
    changeFactor = 0.5,
    initialDeviation = 0.5 * changeFactor * intitialValue,
    dataTrend = 'increasing',
    startYear = (new Date()).getFullYear(),
    ...rest
  } = config;
  const ret = new Array(length);
  const linearGen = random.randomNormal(
    intitialValue * changeFactor,
    initialDeviation * changeFactor
  );
  const expChange = Math.pow(1 / changeFactor, 1 / length);
  const expGen = random.randomNormal(expChange, 0.01);

  const yearProducer = {
    initYear: startYear,
    currYear: startYear - length,
    next() {
      this.currYear++;
      return this.currYear;
    }
  };

  const trend = {
    increasing(p) { return p + linearGen(); },
    decreasing(p) { return p - linearGen(); },
    exponentialGrowth(p) { return p * expGen(); },
    exponentialDecay(p) { return p * 1 / expGen(); }
  };

  let prev = intitialValue;

  const valueProducer = (() => {
    return () => {
      const newVal = trend[dataTrend](prev);
      prev = newVal;
      let val;
      let useNum;

      switch (dataQuality) {
        case 'best':
          val = newVal;
          break;
        case 'mixed':
          useNum = random.randomUniform()() < 0.75 ? true : false; // 75% chance of true
          val = useNum ? newVal : null;
          break;
        case 'worst':
          useNum = random.randomUniform()() < 0.25 ? true : false; // 25% chance of true
          val = useNum ? newVal : null;
          break;
        default:
          val = newVal;
      }
      return val;
    };
  })();

  return map(ret, () => {
    const value = valueProducer();
    return {
      [keyField]: useDates ? yearProducer.next() : cuid(), // collision-resistant string id
      [valueField]: value,
      ub: value + initialDeviation,
      lb: value - initialDeviation,
      ...rest
    };
  });
};
<commit_msg>made fix<commit_after>import { map } from 'lodash';
import cuid from 'cuid';
import random from 'd3-random';

/**
* Random data generator
* @param {Object} config -> configuration object with the following properties:
*  {String} dataQuality -> one of 'best'|'worst'|'mixed'
*    'best' = no data quality issues (no null or missing values)
*    'worst' = a lot of data quality issues (lots of null or missing values)
*    'mixed' = some null values
*  {Number} length -> number of data
*  {String} keyField -> a field of datum that uniquely identifies it
*  {String} valueField -> a field of datum that holds the "primary" datum (e.g., 'value' or 'mean')
*  {Boolean} useDates -> whether to use dates for valueField
*  {Number} startYear -> if (useDates), values will start at startYear
*  {Number} intitialValue -> seed output value
*  {Number} changeFactor -> parameter to adjust shape of data
*  {Number} initialDeviation -> parameter to adjust volitilty of data
*  {String} dataTrend -> one of 'increasing'|'decreasing'|'exponentialGrowth'|'exponentialDecay'
*  {Any} any other property that will be passed directly to individual datum
* @return {Array} array of datum objects
*/
export const dataGenerator = (config = {}) => {
  const {
    dataQuality = 'best',
    length = 200,
    keyField = 'location_id',
    valueField = 'value',
    useDates = false,
    intitialValue = 25000,
    changeFactor = 0.5,
    initialDeviation = 0.5 * changeFactor * intitialValue,
    dataTrend = 'increasing',
    startYear = (new Date()).getFullYear(),
    ...rest
  } = config;
  const ret = new Array(length);
  const linearGen = random.randomNormal(
    intitialValue * changeFactor,
    initialDeviation * changeFactor
  );
  const expChange = Math.pow(1 / changeFactor, 1 / length);
  const expGen = random.randomNormal(expChange, 0.01);

  const yearProducer = {
    initYear: startYear,
    currYear: startYear - length,
    next() {
      this.currYear++;
      return this.currYear;
    }
  };

  const trend = {
    increasing(p) { return p + linearGen(); },
    decreasing(p) { return p - linearGen(); },
    exponentialGrowth(p) { return p * expGen(); },
    exponentialDecay(p) { return p * 1 / expGen(); }
  };

  let prev = intitialValue;

  const valueProducer = (() => {
    return () => {
      const newVal = trend[dataTrend](prev);
      prev = newVal;
      let val;
      let useNum;

      switch (dataQuality) {
        case 'best':
          val = newVal;
          break;
        case 'mixed':
          useNum = random.randomUniform()() < 0.75; // 75% chance of true
          val = useNum ? newVal : null;
          break;
        case 'worst':
          useNum = random.randomUniform()() < 0.25; // 25% chance of true
          val = useNum ? newVal : null;
          break;
        default:
          val = newVal;
      }
      return val;
    };
  })();

  return map(ret, () => {
    const value = valueProducer();
    return {
      [keyField]: useDates ? yearProducer.next() : cuid(), // collision-resistant string id
      [valueField]: value,
      ub: value + initialDeviation,
      lb: value - initialDeviation,
      ...rest
    };
  });
};
<|endoftext|>


<commit_before>import * as internal from "../../src/base/internal.js";
import * as dom from "../../src/core/dom.js";
import * as template from "../../src/core/template.js";
import PlainListBox from "../../src/plain/PlainListBox.js";

class DataListBox extends PlainListBox {
  get data() {
    return this[internal.state].data;
  }
  set data(data) {
    this[internal.setState]({ data });
  }

  get [internal.defaultState]() {
    return Object.assign(super[internal.defaultState], {
      data: null,
      itemPartType: "div",
      items: null,
    });
  }

  [internal.render](changed) {
    super[internal.render](changed);

    if (changed.items) {
      const slot = this[internal.ids].slot;
      if (slot) {
        const items = this[internal.state].items || [];
        dom.updateChildNodes(slot, items);
      }
    }
  }

  [internal.stateEffects](state, changed) {
    const effects = super[internal.stateEffects]
      ? super[internal.stateEffects](state, changed)
      : {};

    if (changed.data) {
      const { data, itemPartType } = state;
      const items =
        data == null
          ? null
          : data.map((obj) => {
              const element = template.createElement(itemPartType);
              element.textContent = obj;
              return element;
            });
      Object.assign(effects, { items });
    }

    return effects;
  }

  get [internal.template]() {
    const result = super[internal.template];

    const defaultSlot = result.content.querySelector("slot:not([name])");
    if (defaultSlot) {
      defaultSlot.id = "slot";
      defaultSlot.name = uuidv4();
    }

    return result;
  }
}

// Generate a UUID
// https://stackoverflow.com/a/2117523/76472
function uuidv4() {
  return `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, (c) =>
    (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16)
  );
}

export default DataListBox;
customElements.define("data-list-box", DataListBox);
<commit_msg>Expose dataItemAdapter to configure mapping of data to item.<commit_after>import * as internal from "../../src/base/internal.js";
import * as dom from "../../src/core/dom.js";
import * as template from "../../src/core/template.js";
import PlainListBox from "../../src/plain/PlainListBox.js";

class DataListBox extends PlainListBox {
  get data() {
    return this[internal.state].data;
  }
  set data(data) {
    this[internal.setState]({ data });
  }

  get dataItemAdapter() {
    return this[internal.state].dataItemAdapter;
  }
  set dataItemAdapter(dataItemAdapter) {
    this[internal.setState]({ dataItemAdapter });
  }

  get [internal.defaultState]() {
    return Object.assign(super[internal.defaultState], {
      data: null,
      dataItemAdapter: defaultDataItemAdapter,
      itemPartType: "div",
      items: null,
    });
  }

  [internal.render](changed) {
    super[internal.render](changed);

    if (changed.items) {
      const slot = this[internal.ids].slot;
      if (slot) {
        const items = this[internal.state].items || [];
        dom.updateChildNodes(slot, items);
      }
    }
  }

  [internal.stateEffects](state, changed) {
    const effects = super[internal.stateEffects]
      ? super[internal.stateEffects](state, changed)
      : {};

    if (changed.data || changed.dataItemAdapter || changed.itemPartType) {
      const { data, dataItemAdapter, itemPartType } = state;
      const items =
        data == null
          ? null
          : data.map((entry) => {
              const element = template.createElement(itemPartType);
              dataItemAdapter(entry, element);
              return element;
            });
      Object.assign(effects, { items });
    }

    return effects;
  }

  get [internal.template]() {
    const result = super[internal.template];

    const defaultSlot = result.content.querySelector("slot:not([name])");
    if (defaultSlot) {
      defaultSlot.id = "slot";
      defaultSlot.name = uuidv4();
    }

    return result;
  }
}

function defaultDataItemAdapter(data, item) {
  item.textContent = data.toString();
}

// Generate a UUID
// https://stackoverflow.com/a/2117523/76472
function uuidv4() {
  return `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, (c) =>
    (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16)
  );
}

export default DataListBox;
customElements.define("data-list-box", DataListBox);
<|endoftext|>