<commit_before>/**
  @module ember-flexberry-gis
*/

import Ember from 'ember';
import DynamicPropertiesMixin from 'ember-flexberry-gis/mixins/dynamic-properties';
import LeafletOptionsMixin from 'ember-flexberry-gis/mixins/leaflet-options';
import LeafletPropertiesMixin from 'ember-flexberry-gis/mixins/leaflet-properties';

const { assert } = Ember;

/**
  BaseLayer component for other flexberry-gis layers.
  @class BaseLayerComponent
  @extends Ember.Component
  @uses DynamicPropertiesMixin
  @uses LeafletOptionsMixin
  @uses LeafletPropertiesMixin
 */
export default Ember.Component.extend(
  DynamicPropertiesMixin,
  LeafletOptionsMixin,
  LeafletPropertiesMixin, {

    /**
      Leaflet layer object init by settings from model.

      @property _leafletObject
      @type L.Layer
      @default null
      @private
     */
    _leafletObject: undefined,

    /**
      Overload wrapper tag name for disabling wrapper.
     */
    tagName: '',

    /**
      Leaflet map.

      @property leafletMap
      @type L.Map
      @default null
    */
    leafletMap: null,

    /**
      Leaflet container for layers.

      @property leafletContainer
      @type L.Map|L.LayerGroup
      @default null
    */
    leafletContainer: null,

    /**
      Layer metadata.

      @property layerModel
      @type Object
      @default null
    */
    layerModel: null,

    /**
      This layer index, used for layer ordering in Map.
      @property index
      @type Int
      @default null
     */
    index: null,

    /**
      Call leaflet layer setZIndex if it presents.
      @method setZIndex
     */
    setZIndex: Ember.observer('index', function () {
      let layer = this.get('_leafletObject');
      if (layer && layer.setZIndex) {
        layer.setZIndex(this.get('index'));
      }
    }),

    /**
      Flag, indicates visible or not current layer on map.
      @property visibility
      @type Boolean
      @default null
     */
    visibility: null,

    /**
      Layer's coordinate reference system (CRS).

      @property crs
      @type L.CRS
      @readOnly
    */
    crs: Ember.computed('layerModel.crs', 'leafletMap.options.crs', function () {
      let crs = this.get('layerModel.crs');
      if (Ember.isNone(crs)) {
        crs = this.get('leafletMap.options.crs');
      }

      return crs;
    }),

    /**
      Handles 'flexberry-map:identify' event of leaflet map.

      @method _identify
      @param {Object} e Event object.
      @param {L.Rectangle} e.boundingBox Leaflet layer
      representing bounding box within which layer's objects must be identified.
      @param {L.LatLng} e.latlng Center of the bounding box.
      @param {Object[]} layers Objects describing those layers which must be identified.
      @param {Object[]} results Objects describing identification results.
      Every result-object has the following structure: { layer: ..., features: [...] },
      where 'layer' is metadata of layer related to identification result, features is array
      containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
      @private
    */
    _identify(e) {
      let shouldIdentify = Ember.A(e.layers || []).contains(this.get('layerModel'));
      if (!shouldIdentify) {
        return;
      }

      // Call public identify method, if layer should be identified.
      this.identify(e);
    },

    /**
      Handles 'flexberry-map:search' event of leaflet map.

      @method search
      @param {Object} e Event object.
      @param {L.LatLng} e.latlng Center of the search area.
      @param {Object[]} layerModel Object describing layer that must be searched.
      @param {Object} searchOptions Search options related to layer type.
      @param {Object} results Hash containing search results.
      @param {Object[]} results.features Array containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
    */
    _search(e) {
      let shouldSearch = typeof (e.filter) === 'function' && e.filter(this.get('layerModel'));
      if (!shouldSearch) {
        return;
      }

      // Call public search method, if layer should be searched.
      e.results.push({
        layerModel: this.get('layerModel'),
        features: this.search(e)
      });
    },

    /**
      Handles 'flexberry-map:query' event of leaflet map.

      @method _query
      @param {Object} e Event object.
      @param {Object} queryFilter Object with query filter paramteres
      @param {Object[]} results.features Array containing leaflet layers objects
      or a promise returning such array.
    */
    _query(e) {
      let layerLinks = this.get('layer.layerLink');

      if (!layerLinks.length) {
        return;
      }

      // Call public query method
      this.query(e);
    },

    /**
      Initializes component.
    */
    init() {
      this._super(...arguments);
      this.set('_leafletObject', this.createLayer());
    },

    /**
      Initializes DOM-related component's properties.
    */
    didInsertElement() {
      this._super(...arguments);
      this.visibilityDidChange();
      this.setZIndex();

      let leafletMap = this.get('leafletMap');
      if (!Ember.isNone(leafletMap)) {
        // Attach custom event-handler.
        leafletMap.on('flexberry-map:identify', this._identify, this);
        leafletMap.on('flexberry-map:search', this._search, this);
        leafletMap.on('flexberry-map:query', this._query, this);
      }
    },

    /**
      Deinitializes DOM-related component's properties.
    */
    willDestroyElement() {
      this._super(...arguments);

      let leafletMap = this.get('leafletMap');
      if (!Ember.isNone(leafletMap)) {
        // Detach custom event-handler.
        leafletMap.off('flexberry-map:identify', this._identify, this);
        leafletMap.off('flexberry-map:search', this._search, this);
        leafletMap.off('flexberry-map:query', this._query, this);
      }

      let leafletContainer = this.get('leafletContainer');
      if (!Ember.isNone(leafletContainer)) {
        leafletContainer.removeLayer(this.get('_leafletObject'));
      }
    },

    /**
      Handles changes in {{#crossLink "BaseLayerComponent/visibility:property"}}'visibility' property{{/crossLink}}.
      Switches layer's visibility.

      @method visibilityDidChange
     */
    visibilityDidChange: Ember.observer('visibility', function () {
      let container = this.get('leafletContainer');

      if (this.get('visibility')) {
        container.addLayer(this.get('_leafletObject'));
      } else {
        container.removeLayer(this.get('_leafletObject'));
      }
    }),

    /**
      Creates leaflet layer related to layer type.

      @method createLayer
    */
    createLayer() {
      assert('BaseLayer\'s \'createLayer\' should be overridden.');
    },

    /**
      Identifies layer's objects inside specified bounding box.

      @method identify
      @param {Object} e Event object.
      @param {L.Rectangle} e.boundingBox Leaflet layer
      representing bounding box within which layer's objects must be identified.
      @param {L.LatLng} e.latlng Center of the bounding box.
      @param {Object[]} layers Objects describing those layers which must be identified.
      @param {Object[]} results Objects describing identification results.
      Every result-object has the following structure: { layer: ..., features: [...] },
      where 'layer' is metadata of layer related to identification result, features is array
      containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
    */
    identify(e) {
      assert('BaseLayer\'s \'identify\' method should be overridden.');
    },

    /**
      Handles 'flexberry-map:search' event of leaflet map.

      @method search
      @param {Object} e Event object.
      @param {L.LatLng} e.latlng Center of the search area.
      @param {Object[]} layer Object describing layer that must be searched.
      @param {Object} searchOptions Search options related to layer type.
      @param {Object} results Hash containing search results.
      @param {Object[]} results.features Array containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
    */
    search(e) {
      assert('BaseLayer\'s \'search\' method should be overridden.');
    },

    /**
      Handles 'flexberry-map:query' event of leaflet map.

      @method _query
      @param {Object} e Event object.
      @param {Object} queryFilter Object with query filter paramteres
      @param {Object[]} results.features Array containing leaflet layers objects
      or a promise returning such array.
    */
    query(e) {
      assert('BaseLayer\'s \'query\' method should be overridden.');
    },

    /**
      Injects (leafelt GeoJSON layers)[http://leafletjs.com/reference-1.0.0.html#geojson] according to current CRS
      into specified (GeoJSON)[http://geojson.org/geojson-spec] feature, features, or featureCollection

      @method injectLeafletLayersIntoGeoJSON
      @param {Object} geojson (GeoJSON)[http://geojson.org/geojson-spec] feature, features, or featureCollection.
      @param {Object} [options] Options of (leafelt GeoJSON layer)[http://leafletjs.com/reference-1.0.0.html#geojson].
      @return (leafelt GeoJSON layer)[http://leafletjs.com/reference-1.0.0.html#geojson].
    */
    injectLeafletLayersIntoGeoJSON(geojson, options) {
      geojson = geojson || {};
      options = options || {};

      let featureCollection = {
        type: 'FeatureCollection',
        features: []
      };

      if (Ember.isArray(geojson)) {
        Ember.set(featureCollection, 'features', geojson);
      } else if (Ember.get(geojson, 'type') === 'Feature') {
        Ember.set(featureCollection, 'features', [geojson]);
      } else if (Ember.get(geojson, 'type') === 'FeatureCollection') {
        featureCollection = geojson;
      }

      let features = Ember.A(Ember.get(featureCollection, 'features') || []);
      if (Ember.get(features, 'length') === 0) {
        return null;
      }

      let crs = this.get('crs');
      Ember.set(options, 'coordsToLatLng', function (coords) {
        let point = new L.Point(coords[0], coords[1]);
        let latlng = crs.projection.unproject(point);
        if (!Ember.isNone(coords[2])) {
          latlng.alt = coords[2];
        }

        return latlng;
      });

      // Define callback method on each feature.
      let originalOnEachFeature = Ember.get(options, 'onEachFeature');
      Ember.set(options, 'onEachFeature', function (feature, leafletLayer) {
        // Remember layer inside feature object.
        Ember.set(feature, 'leafletLayer', leafletLayer);

        // Call user-defined 'onEachFeature' callback.
        if (Ember.typeOf(originalOnEachFeature) === 'function') {
          originalOnEachFeature(feature, leafletLayer);
        }
      });

      // Perform conversion & injection.
      return new L.GeoJSON(featureCollection, options);
    }
  }
);
<commit_msg>Fixed error with instant query for layerLink<commit_after>/**
  @module ember-flexberry-gis
*/

import Ember from 'ember';
import DynamicPropertiesMixin from 'ember-flexberry-gis/mixins/dynamic-properties';
import LeafletOptionsMixin from 'ember-flexberry-gis/mixins/leaflet-options';
import LeafletPropertiesMixin from 'ember-flexberry-gis/mixins/leaflet-properties';

const { assert } = Ember;

/**
  BaseLayer component for other flexberry-gis layers.
  @class BaseLayerComponent
  @extends Ember.Component
  @uses DynamicPropertiesMixin
  @uses LeafletOptionsMixin
  @uses LeafletPropertiesMixin
 */
export default Ember.Component.extend(
  DynamicPropertiesMixin,
  LeafletOptionsMixin,
  LeafletPropertiesMixin, {

    /**
      Leaflet layer object init by settings from model.

      @property _leafletObject
      @type L.Layer
      @default null
      @private
     */
    _leafletObject: undefined,

    /**
      Overload wrapper tag name for disabling wrapper.
     */
    tagName: '',

    /**
      Leaflet map.

      @property leafletMap
      @type L.Map
      @default null
    */
    leafletMap: null,

    /**
      Leaflet container for layers.

      @property leafletContainer
      @type L.Map|L.LayerGroup
      @default null
    */
    leafletContainer: null,

    /**
      Layer metadata.

      @property layerModel
      @type Object
      @default null
    */
    layerModel: null,

    /**
      This layer index, used for layer ordering in Map.
      @property index
      @type Int
      @default null
     */
    index: null,

    /**
      Call leaflet layer setZIndex if it presents.
      @method setZIndex
     */
    setZIndex: Ember.observer('index', function () {
      let layer = this.get('_leafletObject');
      if (layer && layer.setZIndex) {
        layer.setZIndex(this.get('index'));
      }
    }),

    /**
      Flag, indicates visible or not current layer on map.
      @property visibility
      @type Boolean
      @default null
     */
    visibility: null,

    /**
      Layer's coordinate reference system (CRS).

      @property crs
      @type L.CRS
      @readOnly
    */
    crs: Ember.computed('layerModel.crs', 'leafletMap.options.crs', function () {
      let crs = this.get('layerModel.crs');
      if (Ember.isNone(crs)) {
        crs = this.get('leafletMap.options.crs');
      }

      return crs;
    }),

    /**
      Handles 'flexberry-map:identify' event of leaflet map.

      @method _identify
      @param {Object} e Event object.
      @param {L.Rectangle} e.boundingBox Leaflet layer
      representing bounding box within which layer's objects must be identified.
      @param {L.LatLng} e.latlng Center of the bounding box.
      @param {Object[]} layers Objects describing those layers which must be identified.
      @param {Object[]} results Objects describing identification results.
      Every result-object has the following structure: { layer: ..., features: [...] },
      where 'layer' is metadata of layer related to identification result, features is array
      containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
      @private
    */
    _identify(e) {
      let shouldIdentify = Ember.A(e.layers || []).contains(this.get('layerModel'));
      if (!shouldIdentify) {
        return;
      }

      // Call public identify method, if layer should be identified.
      this.identify(e);
    },

    /**
      Handles 'flexberry-map:search' event of leaflet map.

      @method search
      @param {Object} e Event object.
      @param {L.LatLng} e.latlng Center of the search area.
      @param {Object[]} layerModel Object describing layer that must be searched.
      @param {Object} searchOptions Search options related to layer type.
      @param {Object} results Hash containing search results.
      @param {Object[]} results.features Array containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
    */
    _search(e) {
      let shouldSearch = typeof (e.filter) === 'function' && e.filter(this.get('layerModel'));
      if (!shouldSearch) {
        return;
      }

      // Call public search method, if layer should be searched.
      e.results.push({
        layerModel: this.get('layerModel'),
        features: this.search(e)
      });
    },

    /**
      Handles 'flexberry-map:query' event of leaflet map.

      @method _query
      @param {Object} e Event object.
      @param {Object} queryFilter Object with query filter paramteres
      @param {Object[]} results.features Array containing leaflet layers objects
      or a promise returning such array.
    */
    _query(e) {
      let layerLinks = this.get('layer.layerLink');

      if (!Ember.isArray(layerLinks)) {
        return;
      }

      // Call public query method
      this.query(e);
    },

    /**
      Initializes component.
    */
    init() {
      this._super(...arguments);
      this.set('_leafletObject', this.createLayer());
    },

    /**
      Initializes DOM-related component's properties.
    */
    didInsertElement() {
      this._super(...arguments);
      this.visibilityDidChange();
      this.setZIndex();

      let leafletMap = this.get('leafletMap');
      if (!Ember.isNone(leafletMap)) {
        // Attach custom event-handler.
        leafletMap.on('flexberry-map:identify', this._identify, this);
        leafletMap.on('flexberry-map:search', this._search, this);
        leafletMap.on('flexberry-map:query', this._query, this);
      }
    },

    /**
      Deinitializes DOM-related component's properties.
    */
    willDestroyElement() {
      this._super(...arguments);

      let leafletMap = this.get('leafletMap');
      if (!Ember.isNone(leafletMap)) {
        // Detach custom event-handler.
        leafletMap.off('flexberry-map:identify', this._identify, this);
        leafletMap.off('flexberry-map:search', this._search, this);
        leafletMap.off('flexberry-map:query', this._query, this);
      }

      let leafletContainer = this.get('leafletContainer');
      if (!Ember.isNone(leafletContainer)) {
        leafletContainer.removeLayer(this.get('_leafletObject'));
      }
    },

    /**
      Handles changes in {{#crossLink "BaseLayerComponent/visibility:property"}}'visibility' property{{/crossLink}}.
      Switches layer's visibility.

      @method visibilityDidChange
     */
    visibilityDidChange: Ember.observer('visibility', function () {
      let container = this.get('leafletContainer');

      if (this.get('visibility')) {
        container.addLayer(this.get('_leafletObject'));
      } else {
        container.removeLayer(this.get('_leafletObject'));
      }
    }),

    /**
      Creates leaflet layer related to layer type.

      @method createLayer
    */
    createLayer() {
      assert('BaseLayer\'s \'createLayer\' should be overridden.');
    },

    /**
      Identifies layer's objects inside specified bounding box.

      @method identify
      @param {Object} e Event object.
      @param {L.Rectangle} e.boundingBox Leaflet layer
      representing bounding box within which layer's objects must be identified.
      @param {L.LatLng} e.latlng Center of the bounding box.
      @param {Object[]} layers Objects describing those layers which must be identified.
      @param {Object[]} results Objects describing identification results.
      Every result-object has the following structure: { layer: ..., features: [...] },
      where 'layer' is metadata of layer related to identification result, features is array
      containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
    */
    identify(e) {
      assert('BaseLayer\'s \'identify\' method should be overridden.');
    },

    /**
      Handles 'flexberry-map:search' event of leaflet map.

      @method search
      @param {Object} e Event object.
      @param {L.LatLng} e.latlng Center of the search area.
      @param {Object[]} layer Object describing layer that must be searched.
      @param {Object} searchOptions Search options related to layer type.
      @param {Object} results Hash containing search results.
      @param {Object[]} results.features Array containing (GeoJSON feature-objects)[http://geojson.org/geojson-spec.html#feature-objects]
      or a promise returning such array.
    */
    search(e) {
      assert('BaseLayer\'s \'search\' method should be overridden.');
    },

    /**
      Handles 'flexberry-map:query' event of leaflet map.

      @method _query
      @param {Object} e Event object.
      @param {Object} queryFilter Object with query filter paramteres
      @param {Object[]} results.features Array containing leaflet layers objects
      or a promise returning such array.
    */
    query(e) {
      assert('BaseLayer\'s \'query\' method should be overridden.');
    },

    /**
      Injects (leafelt GeoJSON layers)[http://leafletjs.com/reference-1.0.0.html#geojson] according to current CRS
      into specified (GeoJSON)[http://geojson.org/geojson-spec] feature, features, or featureCollection

      @method injectLeafletLayersIntoGeoJSON
      @param {Object} geojson (GeoJSON)[http://geojson.org/geojson-spec] feature, features, or featureCollection.
      @param {Object} [options] Options of (leafelt GeoJSON layer)[http://leafletjs.com/reference-1.0.0.html#geojson].
      @return (leafelt GeoJSON layer)[http://leafletjs.com/reference-1.0.0.html#geojson].
    */
    injectLeafletLayersIntoGeoJSON(geojson, options) {
      geojson = geojson || {};
      options = options || {};

      let featureCollection = {
        type: 'FeatureCollection',
        features: []
      };

      if (Ember.isArray(geojson)) {
        Ember.set(featureCollection, 'features', geojson);
      } else if (Ember.get(geojson, 'type') === 'Feature') {
        Ember.set(featureCollection, 'features', [geojson]);
      } else if (Ember.get(geojson, 'type') === 'FeatureCollection') {
        featureCollection = geojson;
      }

      let features = Ember.A(Ember.get(featureCollection, 'features') || []);
      if (Ember.get(features, 'length') === 0) {
        return null;
      }

      let crs = this.get('crs');
      Ember.set(options, 'coordsToLatLng', function (coords) {
        let point = new L.Point(coords[0], coords[1]);
        let latlng = crs.projection.unproject(point);
        if (!Ember.isNone(coords[2])) {
          latlng.alt = coords[2];
        }

        return latlng;
      });

      // Define callback method on each feature.
      let originalOnEachFeature = Ember.get(options, 'onEachFeature');
      Ember.set(options, 'onEachFeature', function (feature, leafletLayer) {
        // Remember layer inside feature object.
        Ember.set(feature, 'leafletLayer', leafletLayer);

        // Call user-defined 'onEachFeature' callback.
        if (Ember.typeOf(originalOnEachFeature) === 'function') {
          originalOnEachFeature(feature, leafletLayer);
        }
      });

      // Perform conversion & injection.
      return new L.GeoJSON(featureCollection, options);
    }
  }
);
<|endoftext|>


<commit_before>import Ember from 'ember'
import computed from 'ember-computed-decorators'
import layout from '../templates/components/frost-sort'
import _ from 'lodash/lodash'

const {Component, A, isEmpty} = Ember

export default Component.extend({
  layout: layout,
  classNames: ['frost-sort'],

  @computed
  filterArray () {
    if (_.isEmpty(this.get('sortParams'))) {
      return A()
    } else {
      let tempFilterArray = A()
      this.get('sortParams').map(function (param) {
        tempFilterArray.addObject(Ember.Object.create({
          id: tempFilterArray.length + 1,
          value: param.value,
          direction: param.direction
        }))
      })
      return tempFilterArray
    }
  },

  @computed
  hideClass () {
    return _.isEqual(this.get('filterArray').length,
      this.get('sortableProperties').length) ? 'button-hide' : ''
  },

  @computed('filterArray.[]')
  isRemoveVisible (filterArray) {
    return filterArray.length !== 0
  },

  @computed('filterArray.@each.value')
  unselected (filterArray) {
    if (isEmpty(filterArray)) {
      return this.get('sortableProperties')
    }

    let selectedProperties = filterArray.mapBy('value')
    return this.get('sortableProperties').filter(function (sortListItem) {
      return !_.includes(selectedProperties, sortListItem.value)
    })
  },

  actions: {
    addFilter () {
      if (this.get('filterArray').length >= (this.get('sortableProperties').length) - 1) {
        this.set('hideClass', 'button-hide')
      }
      this.get('filterArray').addObject(Ember.Object.create({
        id: this.get('filterArray').length + 1,
        value: '',
        direction: ':asc'
      }))
    },

    removeFilter () {
      this.get('filterArray').popObject()
      this.get('onChange')(this.get('filterArray'))
      if (this.get('filterArray').length < this.get('sortableProperties').length) {
        this.set('hideClass', '')
      }
    },

    sortArrayChange (attrs) {
      this.get('filterArray').findBy('id', attrs.id).setProperties({
        value: attrs.value,
        direction: attrs.direction
      })
      this.get('onChange')(this.get('filterArray'))
    }
  }
})
<commit_msg>Fixing unique id issues for embedded select components<commit_after>import Ember from 'ember'
import computed from 'ember-computed-decorators'
import layout from '../templates/components/frost-sort'
import _ from 'lodash/lodash'

const {Component, A, isEmpty} = Ember

export default Component.extend({
  layout: layout,
  classNames: ['frost-sort'],

  @computed
  filterArray () {
    if (_.isEmpty(this.get('sortParams'))) {
      return A()
    } else {
      let tempFilterArray = A()
      this.get('sortParams').map((param) => {
        tempFilterArray.addObject(Ember.Object.create({
          id: `${this.get('elementId')}_${tempFilterArray.length + 1}`,
          value: param.value,
          direction: param.direction
        }))
      })
      return tempFilterArray
    }
  },

  @computed
  hideClass () {
    return _.isEqual(this.get('filterArray').length,
      this.get('sortableProperties').length) ? 'button-hide' : ''
  },

  @computed('filterArray.[]')
  isRemoveVisible (filterArray) {
    return filterArray.length !== 0
  },

  @computed('filterArray.@each.value')
  unselected (filterArray) {
    if (isEmpty(filterArray)) {
      return this.get('sortableProperties')
    }

    let selectedProperties = filterArray.mapBy('value')
    return this.get('sortableProperties').filter(function (sortListItem) {
      return !_.includes(selectedProperties, sortListItem.value)
    })
  },

  actions: {
    addFilter () {
      if (this.get('filterArray').length >= (this.get('sortableProperties').length) - 1) {
        this.set('hideClass', 'button-hide')
      }
      this.get('filterArray').addObject(Ember.Object.create({
        id: this.get('filterArray').length + 1,
        value: '',
        direction: ':asc'
      }))
    },

    removeFilter () {
      this.get('filterArray').popObject()
      this.get('onChange')(this.get('filterArray'))
      if (this.get('filterArray').length < this.get('sortableProperties').length) {
        this.set('hideClass', '')
      }
    },

    sortArrayChange (attrs) {
      this.get('filterArray').findBy('id', attrs.id).setProperties({
        value: attrs.value,
        direction: attrs.direction
      })
      this.get('onChange')(this.get('filterArray'))
    }
  }
})
<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program 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 License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

module.exports = function(pool, opts) {

  MS_IN_HOUR = 3600000;

  MS_IN_MIN = 60 * 1000;

  var opts = opts || {};

  var defaults = {
    classes: {
      'very-low': {'boundary': 60},
      'low': {'boundary': 80, 'tooltip': 'smbg_tooltip_low.svg'},
      'target': {'boundary': 180, 'tooltip': 'smbg_tooltip_target.svg'},
      'high': {'boundary': 200, 'tooltip': 'smbg_tooltip_high.svg'},
      'very-high': {'boundary': 300}
    },
    size: 16,
    rectWidth: 32,
    xScale: pool.xScale().copy()
  };

  _.defaults(opts, defaults);

  function smbg(selection) {
    selection.each(function(currentData) {
      var circles = d3.select(this)
        .selectAll('g')
        .data(currentData, function(d) {
          return d.id;
        });
      var circleGroups = circles.enter()
        .append('g')
        .attr('class', 'd3-smbg-time-group');
      circleGroups.append('image')
        .attr({
          'xlink:href': function(d) {
            if (d.value <= opts.classes['very-low']['boundary']) {
              return '../img/smbg/very_low.svg';
            }
            else if ((d.value > opts.classes['very-low']['boundary']) && (d.value <= opts.classes['low']['boundary'])) {
              return '../img/smbg/low.svg';
            }
            else if ((d.value > opts.classes['low']['boundary']) && (d.value <= opts.classes['target']['boundary'])) {
              return '../img/smbg/target.svg';
            }
            else if ((d.value > opts.classes['target']['boundary']) && (d.value <= opts.classes['high']['boundary'])) {
              return '../img/smbg/high.svg';
            }
            else if (d.value > opts.classes['high']['boundary']) {
              return '../img/smbg/very_high.svg';
            }
          },
          'x': function(d) {
            var localTime = new Date(d.normalTime);
            var hour = localTime.getUTCHours();
            var min = localTime.getUTCMinutes();
            var sec = localTime.getUTCSeconds();
            var msec = localTime.getUTCMilliseconds();
            var t = hour * MS_IN_HOUR + min * MS_IN_MIN + sec * 1000 + msec;
            return opts.xScale(t) - opts.size / 2;
          },
          'y': function(d) {
            return pool.height() / 2 - opts.size / 2;
          },
          'width': opts.size,
          'height': opts.size,
          'id': function(d) {
            return 'smbg_time_' + d.id;
          },
          'class': function(d) {
            if (d.value <= opts.classes['low']['boundary']) {
              return 'd3-bg-low';
            }
            else if ((d.value > opts.classes['low']['boundary']) && (d.value <= opts.classes['target']['boundary'])) {
              return 'd3-bg-target';
            }
            else if (d.value > opts.classes['target']['boundary']) {
              return 'd3-bg-high';
            }
          }
        })
        .classed({'d3-image': true, 'd3-smbg-time': true, 'd3-image-smbg': true})
        .on('dblclick', function(d) {
          d3.event.stopPropagation(); // silence the click-and-drag listener
          opts.emitter.emit('selectSMBG', d.normalTime);
        });

      circleGroups.append('rect')
        .style('display', 'none')
        .attr({
          'x': function(d) {
            var localTime = new Date(d.normalTime);
            var hour = localTime.getUTCHours();
            var min = localTime.getUTCMinutes();
            var sec = localTime.getUTCSeconds();
            var msec = localTime.getUTCMilliseconds();
            var t = hour * MS_IN_HOUR + min * MS_IN_MIN + sec * 1000 + msec;
            return opts.xScale(t) - opts.rectWidth / 2;
          },
          'y': 0,
          'width': opts.size * 2,
          'height': pool.height() / 2,
          'class': 'd3-smbg-numbers d3-rect-smbg d3-smbg-time'
        });

      // NB: cannot do same display: none strategy because dominant-baseline attribute cannot be applied
      circleGroups.append('text')
        .attr({
          'x': function(d) {
            var localTime = new Date(d.normalTime);
            var hour = localTime.getUTCHours();
            var min = localTime.getUTCMinutes();
            var sec = localTime.getUTCSeconds();
            var msec = localTime.getUTCMilliseconds();
            var t = hour * MS_IN_HOUR + min * MS_IN_MIN + sec * 1000 + msec;
            return opts.xScale(t);
          },
          'y': pool.height() / 4,
          'opacity': '0',
          'class': 'd3-smbg-numbers d3-text-smbg d3-smbg-time'
        })
        .text(function(d) {
          return d.value;
        });

      circles.exit().remove();

      opts.emitter.on('numbers', function(toggle) {
        if (toggle === 'show') {
          d3.selectAll('.d3-rect-smbg')
            .style('display', 'inline');
          d3.selectAll('.d3-text-smbg')
            .transition()
            .duration(500)
            .attr('opacity', 1);
          d3.selectAll('.d3-image-smbg')
            .transition()
            .duration(500)
            .attr({
              'height': opts.size * 0.75,
              'y': pool.height() / 2
            });
        }
        else if (toggle === 'hide') {
          d3.selectAll('.d3-rect-smbg')
            .style('display', 'none');
          d3.selectAll('.d3-text-smbg')
            .transition()
            .duration(500)
            .attr('opacity', 0);
          d3.selectAll('.d3-image-smbg')
            .transition()
            .duration(500)
            .attr({
              'height': opts.size,
              'y': pool.height() / 2 - opts.size / 2
            });
        }
      });
    });
  }

  return smbg;
};<commit_msg>jshint fixes<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2014, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program 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 License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

module.exports = function(pool, opts) {

  MS_IN_HOUR = 3600000;

  MS_IN_MIN = 60 * 1000;

  opts = opts || {};

  var defaults = {
    classes: {
      'very-low': {'boundary': 60},
      'low': {'boundary': 80, 'tooltip': 'smbg_tooltip_low.svg'},
      'target': {'boundary': 180, 'tooltip': 'smbg_tooltip_target.svg'},
      'high': {'boundary': 200, 'tooltip': 'smbg_tooltip_high.svg'},
      'very-high': {'boundary': 300}
    },
    size: 16,
    rectWidth: 32,
    xScale: pool.xScale().copy()
  };

  _.defaults(opts, defaults);

  function smbg(selection) {
    selection.each(function(currentData) {
      var circles = d3.select(this)
        .selectAll('g')
        .data(currentData, function(d) {
          return d.id;
        });
      var circleGroups = circles.enter()
        .append('g')
        .attr('class', 'd3-smbg-time-group');
      circleGroups.append('image')
        .attr({
          'xlink:href': function(d) {
            if (d.value <= opts.classes['very-low']['boundary']) {
              return '../img/smbg/very_low.svg';
            }
            else if ((d.value > opts.classes['very-low']['boundary']) && (d.value <= opts.classes['low']['boundary'])) {
              return '../img/smbg/low.svg';
            }
            else if ((d.value > opts.classes['low']['boundary']) && (d.value <= opts.classes['target']['boundary'])) {
              return '../img/smbg/target.svg';
            }
            else if ((d.value > opts.classes['target']['boundary']) && (d.value <= opts.classes['high']['boundary'])) {
              return '../img/smbg/high.svg';
            }
            else if (d.value > opts.classes['high']['boundary']) {
              return '../img/smbg/very_high.svg';
            }
          },
          'x': function(d) {
            var localTime = new Date(d.normalTime);
            var hour = localTime.getUTCHours();
            var min = localTime.getUTCMinutes();
            var sec = localTime.getUTCSeconds();
            var msec = localTime.getUTCMilliseconds();
            var t = hour * MS_IN_HOUR + min * MS_IN_MIN + sec * 1000 + msec;
            return opts.xScale(t) - opts.size / 2;
          },
          'y': function(d) {
            return pool.height() / 2 - opts.size / 2;
          },
          'width': opts.size,
          'height': opts.size,
          'id': function(d) {
            return 'smbg_time_' + d.id;
          },
          'class': function(d) {
            if (d.value <= opts.classes['low']['boundary']) {
              return 'd3-bg-low';
            }
            else if ((d.value > opts.classes['low']['boundary']) && (d.value <= opts.classes['target']['boundary'])) {
              return 'd3-bg-target';
            }
            else if (d.value > opts.classes['target']['boundary']) {
              return 'd3-bg-high';
            }
          }
        })
        .classed({'d3-image': true, 'd3-smbg-time': true, 'd3-image-smbg': true})
        .on('dblclick', function(d) {
          d3.event.stopPropagation(); // silence the click-and-drag listener
          opts.emitter.emit('selectSMBG', d.normalTime);
        });

      circleGroups.append('rect')
        .style('display', 'none')
        .attr({
          'x': function(d) {
            var localTime = new Date(d.normalTime);
            var hour = localTime.getUTCHours();
            var min = localTime.getUTCMinutes();
            var sec = localTime.getUTCSeconds();
            var msec = localTime.getUTCMilliseconds();
            var t = hour * MS_IN_HOUR + min * MS_IN_MIN + sec * 1000 + msec;
            return opts.xScale(t) - opts.rectWidth / 2;
          },
          'y': 0,
          'width': opts.size * 2,
          'height': pool.height() / 2,
          'class': 'd3-smbg-numbers d3-rect-smbg d3-smbg-time'
        });

      // NB: cannot do same display: none strategy because dominant-baseline attribute cannot be applied
      circleGroups.append('text')
        .attr({
          'x': function(d) {
            var localTime = new Date(d.normalTime);
            var hour = localTime.getUTCHours();
            var min = localTime.getUTCMinutes();
            var sec = localTime.getUTCSeconds();
            var msec = localTime.getUTCMilliseconds();
            var t = hour * MS_IN_HOUR + min * MS_IN_MIN + sec * 1000 + msec;
            return opts.xScale(t);
          },
          'y': pool.height() / 4,
          'opacity': '0',
          'class': 'd3-smbg-numbers d3-text-smbg d3-smbg-time'
        })
        .text(function(d) {
          return d.value;
        });

      circles.exit().remove();

      opts.emitter.on('numbers', function(toggle) {
        if (toggle === 'show') {
          d3.selectAll('.d3-rect-smbg')
            .style('display', 'inline');
          d3.selectAll('.d3-text-smbg')
            .transition()
            .duration(500)
            .attr('opacity', 1);
          d3.selectAll('.d3-image-smbg')
            .transition()
            .duration(500)
            .attr({
              'height': opts.size * 0.75,
              'y': pool.height() / 2
            });
        }
        else if (toggle === 'hide') {
          d3.selectAll('.d3-rect-smbg')
            .style('display', 'none');
          d3.selectAll('.d3-text-smbg')
            .transition()
            .duration(500)
            .attr('opacity', 0);
          d3.selectAll('.d3-image-smbg')
            .transition()
            .duration(500)
            .attr({
              'height': opts.size,
              'y': pool.height() / 2 - opts.size / 2
            });
        }
      });
    });
  }

  return smbg;
};<|endoftext|>


<commit_before>/* eslint-disable no-unused-vars */
/* global async getParameterByName */
function build_kugou() {
  function kg_convert_song(song) {
    const track = {
      id: `kgtrack_${song.FileHash}`,
      title: song.SongName,
      artist: '',
      artist_id: '',
      album: song.AlbumName,
      album_id: `kgalbum_${song.AlbumID}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${song.FileHash}&album_id=${song.AlbumID}`,
      img_url: '',
      // url: `kgtrack_${song.FileHash}`,
      lyric_url: song.FileHash,
    };
    let singer_id = song.SingerId;
    let singer_name = song.SingerName;
    if (song.SingerId instanceof Array) {
      [singer_id] = singer_id;
      [singer_name] = singer_name.split('、');
    }
    track.artist = singer_name;
    track.artist_id = `kgartist_${singer_id}`;
    return track;
  }

  function async_process_list(data_list, handler, handler_extra_param_list, callback) {
    const fnDict = {};
    data_list.forEach((item, index) => {
      fnDict[index] = (cb) => handler(index, item, handler_extra_param_list, cb);
    });
    async.parallel(fnDict,
      (err, results) => callback(null, data_list.map((item, index) => results[index])));
  }

  function kg_render_search_result_item(index, item, params, callback) {
    const track = kg_convert_song(item);
    // Add singer img
    const url = `${'http://www.kugou.com/yy/index.php?'
            + 'r=play/getdata&hash='}${track.lyric_url}`;
    axios.get(url).then((response) => {
      const { data } = response;
      track.img_url = data.data.img;
      callback(null, track);
    });
  }

  function kg_search(url) { // eslint-disable-line no-unused-vars
    const keyword = getParameterByName('keywords', url);
    const curpage = getParameterByName('curpage', url);
    const searchType = getParameterByName('type', url);
    if (searchType === '1') {
      return {
        success(fn) {
          return fn({
            result: [],
            total: 0,
            type: searchType,
          });
        },
      };
    }
    return {
      success(fn) {
        const target_url = `${'http://songsearch.kugou.com/'
          + 'song_search_v2?keyword='}${keyword}&page=${curpage}`;
        axios.get(target_url)
          .then((response) => {
            const { data } = response;
            async_process_list(data.data.lists, kg_render_search_result_item, [],
              (err, tracks) => fn({
                result: tracks,
                total: data.data.total,
                type: searchType,
              }));
          })
          .catch(() => fn({
            result: [],
            total: 0,
            type: searchType,
          }));
      },
    };
  }

  function kg_render_playlist_result_item(index, item, params, callback) {
    let target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?'
        + 'cmd=playInfo&hash='}${item.hash}`;

    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: '',
      album: '',
      album_id: `kgalbum_${item.album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${item.album_id}`,
      img_url: '',
      // url: `xmtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    // Fix song info
    axios.get(target_url).then((response) => {
      const { data } = response;
      track.title = data.songName;
      track.artist = data.singerId === 0
        ? '未知' : data.singerName;
      track.artist_id = `kgartist_${data.singerId}`;
      if (data.imgUrl !== undefined) {
        track.img_url = data.imgUrl.replace('{size}', '400');
      } else {
        // track['img_url'] = data.imgUrl.replace('{size}', '400');
      }
      // Fix album
      target_url = `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${
        item.album_id}`;
      axios.get(target_url).then((res) => {
        const { data: res_data } = res;
        if (res_data.status && res_data.data !== undefined && res_data.data !== null) {
          track.album = res_data.data.albumname;
        } else {
          track.album = '';
        }
        return callback(null, track);
      });
    });
  }

  function kg_get_playlist(url) { // eslint-disable-line no-unused-vars
    return {
      success(fn) {
        const list_id = getParameterByName('list_id', url).split('_').pop();
        const target_url = `http://m.kugou.com/plist/list/${list_id}?json=true`;

        axios.get(target_url).then((response) => {
          const { data } = response;

          const info = {
            cover_img_url: data.info.list.imgurl
              ? data.info.list.imgurl.replace('{size}', '400') : '',
            title: data.info.list.specialname,
            id: `kgplaylist_${data.info.list.specialid}`,
            source_url: 'http://www.kugou.com/yy/special/single/{size}.html'
              .replace('{size}', data.info.list.specialid),
          };

          async_process_list(data.list.list.info, kg_render_playlist_result_item, [],
            (err, tracks) => fn({
              tracks,
              info,
            }));
        });
      },
    };
  }

  function kg_render_artist_result_item(index, item, params, callback) {
    const info = params[0];
    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: info.id,
      album: '',
      album_id: `kgalbum_${item.album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${item.album_id}`,
      img_url: '',
      // url: `kgtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    const one = item.filename.split('-');
    track.title = one[1].trim();
    track.artist = one[0].trim();
    // Fix album name and img
    const target_url = `${'http://www.kugou.com/yy/index.php?'
            + 'r=play/getdata&hash='}${item.hash}`;
    axios.get(`http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${item.album_id}`)
      .then((response) => {
        const { data } = response;
        if (data.status && data.data !== undefined) {
          track.album = data.data.albumname;
        } else {
          track.album = '';
        }
        axios.get(target_url)
          .then((res) => {
            track.img_url = res.data.data.img;
            callback(null, track);
          });
      });
  }

  function kg_artist(url) { // eslint-disable-line no-unused-vars
    return {
      success(fn) {
        const artist_id = getParameterByName('list_id', url).split('_').pop();
        let target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/info?singerid=${artist_id}`;
        axios.get(target_url)
          .then((response) => {
            const { data } = response;
            const info = {
              cover_img_url: data.data.imgurl.replace('{size}', '400'),
              title: data.data.singername,
              id: `kgartist_${artist_id}`,
              source_url: 'http://www.kugou.com/singer/{id}.html'.replace('{id}', artist_id),
            };
            target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/song?singerid=${
              artist_id}&page=1&pagesize=30`;
            axios.get(target_url)
              .then((res) => {
                async_process_list(res.data.data.info, kg_render_artist_result_item, [info],
                  (err, tracks) => fn({
                    tracks,
                    info,
                  }));
              });
          });
      },
    };
  }

  function getTimestampString() {
    return (new Date()).getTime().toString();
  }

  function getRandomIntString() {
    return (Math.random() * 100).toString().replace(/\D/g, '');
  }

  function getRandomHexString() {
    let result = '';
    const letters = '0123456789abcdef';
    for (let i = 0; i < 16; i += 1) {
      result += letters[(Math.floor(Math.random() * 16))];
    }
    return result;
  }

  function kg_bootstrap_track(sound, track, success, failure) {
    const song_id = track.id.slice('kgtrack_'.length);
    const target_url = `http://m.kugou.com/app/i/getSongInfo.php?cmd=playInfo&hash=${song_id}`;

    axios.get(target_url)
      .then((response) => {
        const { data } = response;
        if (data.url !== '') {
          sound.url = data.url; // eslint-disable-line no-param-reassign
          success();
        } else {
          failure();
        }
      });
  }

  function kg_lyric(url) { // eslint-disable-line no-unused-vars
    const track_id = getParameterByName('track_id', url).split('_').pop();
    const album_id = getParameterByName('album_id', url).split('_').pop();
    const lyric_url = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery&hash=${track_id}&dfid=dfid&mid=mid&platid=4&album_id=${album_id}`;

    return {
      success(fn) {
        axios.get(lyric_url)
          .then((response) => {
            const { data } = response;
            const jsonString = data.slice('jQuery('.length, data.length-1-1);
            const info = JSON.parse(jsonString)
            return fn({
              lyric: info.data.lyrics,
            });
          });
      },
    };
  }

  function kg_render_album_result_item(index, item, params, callback) {
    const info = params[0];
    const album_id = params[1];
    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: '',
      album: info.title,
      album_id: `kgalbum_${album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${album_id}`,
      img_url: '',
      // url: `xmtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    // Fix other data
    const target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?'
            + 'cmd=playInfo&hash='}${item.hash}`;
    axios.get(target_url)
      .then((response) => {
        const { data } = response;
        track.title = data.songName;
        track.artist = data.singerId === 0
          ? '未知' : data.singerName;
        track.artist_id = `kgartist_${data.singerId}`;
        track.img_url = data.imgUrl.replace('{size}', '400');
        callback(null, track);
      });
  }

  function kg_album(url) { // eslint-disable-line no-unused-vars
    return {
      success(fn) {
        const album_id = getParameterByName('list_id', url).split('_').pop();
        let target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/info?'
          + 'albumid='}${album_id}`;

        let info;
        // info
        axios.get(target_url)
          .then((response) => {
            const { data } = response;

            info = {
              cover_img_url: data.data.imgurl.replace('{size}', '400'),
              title: data.data.albumname,
              id: `kgalbum_${data.data.albumid}`,
              source_url: 'http://www.kugou.com/album/{id}.html'
                .replace('{id}', data.data.albumid),
            };

            target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/song?'
            + 'albumid='}${album_id}&page=1&pagesize=-1`;
            axios.get(target_url)
              .then((res) => {
                async_process_list(res.data.data.info, kg_render_album_result_item,
                  [info, album_id],
                  (err, tracks) => fn({
                    tracks,
                    info,
                  }));
              });
          });
      },
    };
  }

  function kg_show_playlist(url) {
    let offset = getParameterByName('offset', url);
    if (offset === undefined) {
      offset = 0;
    }
    // const page = offset / 30 + 1;
    const target_url = `${'http://m.kugou.com/plist/index'
            + '&json=true&page='}${offset}`;
    return {
      success(fn) {
        axios.get(target_url).then((response) => {
          const { data } = response;
          // const total = data.plist.total;
          const result = data.plist.list.info.map((item) => ({
            cover_img_url: item.imgurl ? item.imgurl.replace('{size}', '400') : '',
            title: item.specialname,
            id: `kgplaylist_${item.specialid}`,
            source_url: 'http://www.kugou.com/yy/special/single/{size}.html'.replace('{size}', item.specialid),
          }));
          return fn({
            result,
          });
        });
      },
    };
  }

  function kg_parse_url(url) {
    let result;
    const match = /\/\/www.kugou.com\/yy\/special\/single\/([0-9]+).html/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `kgplaylist_${playlist_id}`,
      };
    }
    return result;
  }

  function get_playlist(url) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_')[0];
    switch (list_id) {
      case 'kgplaylist':
        return kg_get_playlist(url);
      case 'kgalbum':
        return kg_album(url);
      case 'kgartist':
        return kg_artist(url);
      default:
        return null;
    }
  }

  return {
    show_playlist: kg_show_playlist,
    get_playlist,
    parse_url: kg_parse_url,
    bootstrap_track: kg_bootstrap_track,
    search: kg_search,
    lyric: kg_lyric,
  };
}

const kugou = build_kugou(); // eslint-disable-line no-unused-vars
<commit_msg>fix eslint warnings<commit_after>/* eslint-disable no-unused-vars */
/* global async getParameterByName */
function build_kugou() {
  function kg_convert_song(song) {
    const track = {
      id: `kgtrack_${song.FileHash}`,
      title: song.SongName,
      artist: '',
      artist_id: '',
      album: song.AlbumName,
      album_id: `kgalbum_${song.AlbumID}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${song.FileHash}&album_id=${song.AlbumID}`,
      img_url: '',
      // url: `kgtrack_${song.FileHash}`,
      lyric_url: song.FileHash,
    };
    let singer_id = song.SingerId;
    let singer_name = song.SingerName;
    if (song.SingerId instanceof Array) {
      [singer_id] = singer_id;
      [singer_name] = singer_name.split('、');
    }
    track.artist = singer_name;
    track.artist_id = `kgartist_${singer_id}`;
    return track;
  }

  function async_process_list(data_list, handler, handler_extra_param_list, callback) {
    const fnDict = {};
    data_list.forEach((item, index) => {
      fnDict[index] = (cb) => handler(index, item, handler_extra_param_list, cb);
    });
    async.parallel(fnDict,
      (err, results) => callback(null, data_list.map((item, index) => results[index])));
  }

  function kg_render_search_result_item(index, item, params, callback) {
    const track = kg_convert_song(item);
    // Add singer img
    const url = `${'http://www.kugou.com/yy/index.php?'
            + 'r=play/getdata&hash='}${track.lyric_url}`;
    axios.get(url).then((response) => {
      const { data } = response;
      track.img_url = data.data.img;
      callback(null, track);
    });
  }

  function kg_search(url) { // eslint-disable-line no-unused-vars
    const keyword = getParameterByName('keywords', url);
    const curpage = getParameterByName('curpage', url);
    const searchType = getParameterByName('type', url);
    if (searchType === '1') {
      return {
        success(fn) {
          return fn({
            result: [],
            total: 0,
            type: searchType,
          });
        },
      };
    }
    return {
      success(fn) {
        const target_url = `${'http://songsearch.kugou.com/'
          + 'song_search_v2?keyword='}${keyword}&page=${curpage}`;
        axios.get(target_url)
          .then((response) => {
            const { data } = response;
            async_process_list(data.data.lists, kg_render_search_result_item, [],
              (err, tracks) => fn({
                result: tracks,
                total: data.data.total,
                type: searchType,
              }));
          })
          .catch(() => fn({
            result: [],
            total: 0,
            type: searchType,
          }));
      },
    };
  }

  function kg_render_playlist_result_item(index, item, params, callback) {
    let target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?'
        + 'cmd=playInfo&hash='}${item.hash}`;

    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: '',
      album: '',
      album_id: `kgalbum_${item.album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${item.album_id}`,
      img_url: '',
      // url: `xmtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    // Fix song info
    axios.get(target_url).then((response) => {
      const { data } = response;
      track.title = data.songName;
      track.artist = data.singerId === 0
        ? '未知' : data.singerName;
      track.artist_id = `kgartist_${data.singerId}`;
      if (data.imgUrl !== undefined) {
        track.img_url = data.imgUrl.replace('{size}', '400');
      } else {
        // track['img_url'] = data.imgUrl.replace('{size}', '400');
      }
      // Fix album
      target_url = `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${
        item.album_id}`;
      axios.get(target_url).then((res) => {
        const { data: res_data } = res;
        if (res_data.status && res_data.data !== undefined && res_data.data !== null) {
          track.album = res_data.data.albumname;
        } else {
          track.album = '';
        }
        return callback(null, track);
      });
    });
  }

  function kg_get_playlist(url) { // eslint-disable-line no-unused-vars
    return {
      success(fn) {
        const list_id = getParameterByName('list_id', url).split('_').pop();
        const target_url = `http://m.kugou.com/plist/list/${list_id}?json=true`;

        axios.get(target_url).then((response) => {
          const { data } = response;

          const info = {
            cover_img_url: data.info.list.imgurl
              ? data.info.list.imgurl.replace('{size}', '400') : '',
            title: data.info.list.specialname,
            id: `kgplaylist_${data.info.list.specialid}`,
            source_url: 'http://www.kugou.com/yy/special/single/{size}.html'
              .replace('{size}', data.info.list.specialid),
          };

          async_process_list(data.list.list.info, kg_render_playlist_result_item, [],
            (err, tracks) => fn({
              tracks,
              info,
            }));
        });
      },
    };
  }

  function kg_render_artist_result_item(index, item, params, callback) {
    const info = params[0];
    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: info.id,
      album: '',
      album_id: `kgalbum_${item.album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${item.album_id}`,
      img_url: '',
      // url: `kgtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    const one = item.filename.split('-');
    track.title = one[1].trim();
    track.artist = one[0].trim();
    // Fix album name and img
    const target_url = `${'http://www.kugou.com/yy/index.php?'
            + 'r=play/getdata&hash='}${item.hash}`;
    axios.get(`http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${item.album_id}`)
      .then((response) => {
        const { data } = response;
        if (data.status && data.data !== undefined) {
          track.album = data.data.albumname;
        } else {
          track.album = '';
        }
        axios.get(target_url)
          .then((res) => {
            track.img_url = res.data.data.img;
            callback(null, track);
          });
      });
  }

  function kg_artist(url) { // eslint-disable-line no-unused-vars
    return {
      success(fn) {
        const artist_id = getParameterByName('list_id', url).split('_').pop();
        let target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/info?singerid=${artist_id}`;
        axios.get(target_url)
          .then((response) => {
            const { data } = response;
            const info = {
              cover_img_url: data.data.imgurl.replace('{size}', '400'),
              title: data.data.singername,
              id: `kgartist_${artist_id}`,
              source_url: 'http://www.kugou.com/singer/{id}.html'.replace('{id}', artist_id),
            };
            target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/song?singerid=${
              artist_id}&page=1&pagesize=30`;
            axios.get(target_url)
              .then((res) => {
                async_process_list(res.data.data.info, kg_render_artist_result_item, [info],
                  (err, tracks) => fn({
                    tracks,
                    info,
                  }));
              });
          });
      },
    };
  }

  function getTimestampString() {
    return (new Date()).getTime().toString();
  }

  function getRandomIntString() {
    return (Math.random() * 100).toString().replace(/\D/g, '');
  }

  function getRandomHexString() {
    let result = '';
    const letters = '0123456789abcdef';
    for (let i = 0; i < 16; i += 1) {
      result += letters[(Math.floor(Math.random() * 16))];
    }
    return result;
  }

  function kg_bootstrap_track(sound, track, success, failure) {
    const song_id = track.id.slice('kgtrack_'.length);
    const target_url = `http://m.kugou.com/app/i/getSongInfo.php?cmd=playInfo&hash=${song_id}`;

    axios.get(target_url)
      .then((response) => {
        const { data } = response;
        if (data.url !== '') {
          sound.url = data.url; // eslint-disable-line no-param-reassign
          success();
        } else {
          failure();
        }
      });
  }

  function kg_lyric(url) { // eslint-disable-line no-unused-vars
    const track_id = getParameterByName('track_id', url).split('_').pop();
    const album_id = getParameterByName('album_id', url).split('_').pop();
    const lyric_url = `https://wwwapi.kugou.com/yy/index.php?r=play/getdata&callback=jQuery&hash=${track_id}&dfid=dfid&mid=mid&platid=4&album_id=${album_id}`;

    return {
      success(fn) {
        axios.get(lyric_url)
          .then((response) => {
            const { data } = response;
            const jsonString = data.slice('jQuery('.length, data.length - 1 - 1);
            const info = JSON.parse(jsonString);
            return fn({
              lyric: info.data.lyrics,
            });
          });
      },
    };
  }

  function kg_render_album_result_item(index, item, params, callback) {
    const info = params[0];
    const album_id = params[1];
    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: '',
      album: info.title,
      album_id: `kgalbum_${album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${album_id}`,
      img_url: '',
      // url: `xmtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    // Fix other data
    const target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?'
            + 'cmd=playInfo&hash='}${item.hash}`;
    axios.get(target_url)
      .then((response) => {
        const { data } = response;
        track.title = data.songName;
        track.artist = data.singerId === 0
          ? '未知' : data.singerName;
        track.artist_id = `kgartist_${data.singerId}`;
        track.img_url = data.imgUrl.replace('{size}', '400');
        callback(null, track);
      });
  }

  function kg_album(url) { // eslint-disable-line no-unused-vars
    return {
      success(fn) {
        const album_id = getParameterByName('list_id', url).split('_').pop();
        let target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/info?'
          + 'albumid='}${album_id}`;

        let info;
        // info
        axios.get(target_url)
          .then((response) => {
            const { data } = response;

            info = {
              cover_img_url: data.data.imgurl.replace('{size}', '400'),
              title: data.data.albumname,
              id: `kgalbum_${data.data.albumid}`,
              source_url: 'http://www.kugou.com/album/{id}.html'
                .replace('{id}', data.data.albumid),
            };

            target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/song?'
            + 'albumid='}${album_id}&page=1&pagesize=-1`;
            axios.get(target_url)
              .then((res) => {
                async_process_list(res.data.data.info, kg_render_album_result_item,
                  [info, album_id],
                  (err, tracks) => fn({
                    tracks,
                    info,
                  }));
              });
          });
      },
    };
  }

  function kg_show_playlist(url) {
    let offset = getParameterByName('offset', url);
    if (offset === undefined) {
      offset = 0;
    }
    // const page = offset / 30 + 1;
    const target_url = `${'http://m.kugou.com/plist/index'
            + '&json=true&page='}${offset}`;
    return {
      success(fn) {
        axios.get(target_url).then((response) => {
          const { data } = response;
          // const total = data.plist.total;
          const result = data.plist.list.info.map((item) => ({
            cover_img_url: item.imgurl ? item.imgurl.replace('{size}', '400') : '',
            title: item.specialname,
            id: `kgplaylist_${item.specialid}`,
            source_url: 'http://www.kugou.com/yy/special/single/{size}.html'.replace('{size}', item.specialid),
          }));
          return fn({
            result,
          });
        });
      },
    };
  }

  function kg_parse_url(url) {
    let result;
    const match = /\/\/www.kugou.com\/yy\/special\/single\/([0-9]+).html/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `kgplaylist_${playlist_id}`,
      };
    }
    return result;
  }

  function get_playlist(url) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_')[0];
    switch (list_id) {
      case 'kgplaylist':
        return kg_get_playlist(url);
      case 'kgalbum':
        return kg_album(url);
      case 'kgartist':
        return kg_artist(url);
      default:
        return null;
    }
  }

  return {
    show_playlist: kg_show_playlist,
    get_playlist,
    parse_url: kg_parse_url,
    bootstrap_track: kg_bootstrap_track,
    search: kg_search,
    lyric: kg_lyric,
  };
}

const kugou = build_kugou(); // eslint-disable-line no-unused-vars
<|endoftext|>


<commit_before>/**
 * @module ember-paper
 */
import { filter, bool, or } from '@ember/object/computed';

import Component from '@ember/component';
import { computed } from '@ember/object';
import layout from '../templates/components/paper-item';
import RippleMixin from '../mixins/ripple-mixin';
import { ParentMixin } from 'ember-composability-tools';

/**
 * @class PaperItem
 * @extends Ember.Component
 * @uses ParentMixin
 * @uses RippleMixin
 */
export default Component.extend(RippleMixin, ParentMixin, {
  layout,
  tagName: 'md-list-item',
  attributeBindings: ['title'],

  // Ripple Overrides
  rippleContainerSelector: '.md-no-style',
  // disable ripple when we have a primary action or when we don't have a proxied component
  noink: computed('hasPrimaryAction', 'hasProxiedComponent', function() {
    return this.get('hasPrimaryAction') || !this.get('hasProxiedComponent');
  }),

  center: false,
  dimBackground: true,
  outline: false,

  classNameBindings: [
    'hasProxiedComponent:md-proxy-focus', 'shouldBeClickable:md-clickable',
    'focused:md-focused', 'hasPrimaryAction:_md-button-wrap'
  ],
  attributeBindings: ['role', 'tabindex'],
  role: 'listitem',
  tabindex: '-1',

  proxiedComponents: filter('childComponents', function(c) {
    return !c.get('skipProxy');
  }),

  hasProxiedComponent: bool('proxiedComponents.length'),
  shouldBeClickable: or('hasProxiedComponent', 'onClick'),

  hasPrimaryAction: or('onClick', 'href'),

  noProxy: computed('hasPrimaryAction', 'hasProxiedComponent', function() {
    return !this.get('hasPrimaryAction') && !this.get('hasProxiedComponent');
  }),

  secondaryItem: computed('proxiedComponents.[]', function() {
    let proxiedComponents = this.get('proxiedComponents');
    return proxiedComponents.objectAt(0);
  }),

  click() {
    this.get('proxiedComponents').forEach((component) => {
      if (component.processProxy && !component.get('disabled') && (component.get('bubbles') | !this.get('hasPrimaryAction'))) {
        component.processProxy();
      }
    });
  },

  mouseEnter(ev) {
    this.sendAction('onMouseEnter', ev);
  },

  mouseLeave(ev) {
    this.sendAction('onMouseLeave', ev);
  }
});
<commit_msg>remove duplicated key<commit_after>/**
 * @module ember-paper
 */
import { filter, bool, or } from '@ember/object/computed';

import Component from '@ember/component';
import { computed } from '@ember/object';
import layout from '../templates/components/paper-item';
import RippleMixin from '../mixins/ripple-mixin';
import { ParentMixin } from 'ember-composability-tools';

/**
 * @class PaperItem
 * @extends Ember.Component
 * @uses ParentMixin
 * @uses RippleMixin
 */
export default Component.extend(RippleMixin, ParentMixin, {
  layout,
  tagName: 'md-list-item',

  // Ripple Overrides
  rippleContainerSelector: '.md-no-style',
  // disable ripple when we have a primary action or when we don't have a proxied component
  noink: computed('hasPrimaryAction', 'hasProxiedComponent', function() {
    return this.get('hasPrimaryAction') || !this.get('hasProxiedComponent');
  }),

  center: false,
  dimBackground: true,
  outline: false,

  classNameBindings: [
    'hasProxiedComponent:md-proxy-focus', 'shouldBeClickable:md-clickable',
    'focused:md-focused', 'hasPrimaryAction:_md-button-wrap'
  ],
  attributeBindings: ['role', 'tabindex', 'title'],
  role: 'listitem',
  tabindex: '-1',

  proxiedComponents: filter('childComponents', function(c) {
    return !c.get('skipProxy');
  }),

  hasProxiedComponent: bool('proxiedComponents.length'),
  shouldBeClickable: or('hasProxiedComponent', 'onClick'),

  hasPrimaryAction: or('onClick', 'href'),

  noProxy: computed('hasPrimaryAction', 'hasProxiedComponent', function() {
    return !this.get('hasPrimaryAction') && !this.get('hasProxiedComponent');
  }),

  secondaryItem: computed('proxiedComponents.[]', function() {
    let proxiedComponents = this.get('proxiedComponents');
    return proxiedComponents.objectAt(0);
  }),

  click() {
    this.get('proxiedComponents').forEach((component) => {
      if (component.processProxy && !component.get('disabled') && (component.get('bubbles') | !this.get('hasPrimaryAction'))) {
        component.processProxy();
      }
    });
  },

  mouseEnter(ev) {
    this.sendAction('onMouseEnter', ev);
  },

  mouseLeave(ev) {
    this.sendAction('onMouseLeave', ev);
  }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import { property } from './-properties';
import ModelMixin from './-model-mixin';

const {
  Mixin,
  computed
} = Ember;

const model = name => computed(function() {
  let internal = this._internal;
  return internal[name].call(internal, true).model(true);
}).readOnly();

export const loader = name => function(...args) {
  let loader = this.get('loader');
  return loader[name].call(loader, ...args).then(() => this);
};

export default Mixin.create(ModelMixin, {

  database: property('database'),

  filter: model('filter'),
  loader: model('loader'),

  load:   loader('load'),
  reload: loader('reload')

});
<commit_msg>proxy.get('database') fix<commit_after>import Ember from 'ember';
import { property } from './-properties';
import ModelMixin from './-model-mixin';

const {
  Mixin,
  computed
} = Ember;

const model = name => computed(function() {
  let internal = this._internal;
  return internal[name].call(internal, true).model(true);
}).readOnly();

export const loader = name => function(...args) {
  let loader = this.get('loader');
  return loader[name].call(loader, ...args).then(() => this);
};

const database = property('database');

export default Mixin.create(ModelMixin, {

  database: database(),

  filter: model('filter'),
  loader: model('loader'),

  load:   loader('load'),
  reload: loader('reload')

});
<|endoftext|>


<commit_before>"use strict"

var
    cfg        = require('config'),
    GmailModel = require('gmail-model'),
    log4js     = require('log4js')

/*
 * Object variables
 */

var
    gmail,
    log,
    name,
    searches

/**
 * Mailbox model constructor.
 * @param {object}   params - Params to be passed in
 * @param {string}   params.clientSecretFile - full path to the client secret file to be used by google if an access token doesn't yet exist
 * @param {string[]} params.googleScopes - Google drive scopes for which this object instance will have permissions
 * @param {string}   params.name - Name of the mailbox (for logging)
 * @param {string}   params.tokenDir - directory on the local machine in which the google access token lives
 * @param {string}   params.tokenFile - name of file on the local machine that contains the google access token
 * @constructor
 */
function Mailbox (params) {

  // logs

  log4js.configure(cfg.log.log4jsConfigs);

  log = log4js.getLogger(cfg.log.appName);
  log.setLevel(cfg.log.level);

  // Setup the gmail inbox
  this.gmail = new GmailModel({
    clientSecretFile: params.clientSecretFile,
    googleScopes:     params.googleScopes,
    name:             params.name,
    tokenDir:         params.tokenDir,
    tokenFile:        params.tokenFile
  })

  this.name     = params.name
  this.searches = params.searches

  // Log out the searches
  var self = this
  this.searches.forEach( function(el, idx) {
    log.info('[%s] [%s] Search: ', self.name, idx, el)
  })

}

var method = Mailbox.prototype

/**
 * mailbox.getMessages
 *
 * @desc Idetify messages matching search criteria
 *
 * @alias mailbox.getMessages
 *
 * @param  {object} params - Parameters for request (currently unused)
 * @param  {callback} callback - The callback that handles the error response. Returns callback(errMsg)
 * @return {object} errMsg -
 */
method.process = function (params,cb) {

  var mb = this.name

  var self = this;

  self.searches.forEach( function (el, searchIdx) {

    log.info('[%s] [%s] Searching', self.name, searchIdx) ;

    self.gmail.listMessages({
      freetextSearch: el
    }, function (err, messages) {

      if (err) {
        var errMsg = '['+mb+'] ['+searchIdx+']: Error retrieving emails for search: "'+err;
        cb(errMsg)
        return null;
      }

      var numMessages = messages.length
      log.info('[%s] [%s] Trashing %s messages', self.name, searchIdx, numMessages ) ;

      // Bail if there was nothing to delete
      if (messages.length == 0) return;

      var ids = [];
      messages.forEach( function(el,idx) {
        ids.push(el.id)
      })

      // Log out details of the message about to be trashed
      ids.forEach( function(el,idx) {

        // Get message details to improve logging
        self.gmail.getMessage({
          messageId: el,
          format: 'metadata',
          metadataHeaders: ['Subject', 'Date'],
          retFields: ['payload(headers)']
        }, function (err, resp) {

          if (err) {
            var errMsg = '['+mb+'] ['+searchIdx+']: Error getting details for message '+el+': '+err;
            cb(errMsg)
            return null;
          }

          // Get metadata for better logging
          var subject = ''
          var date    = ''
	  var headers = resp.payload.headers

          for (var i = 0; i < headers.length; i++) {
            if (headers[i].name == "Subject") { subject = headers[i].value }
            else if (headers[i].name == "Date") { date = headers[i].value }
          }

          log.info('[%s] [%s] Trashing message (%s) "%s" (%s)', self.name, searchIdx, el, subject, date) ;
	})
      })

      // Trash them
      self.gmail.trashMessages({
        messageIds: ids
      }, function (err,resps) {
        if (err) {
          var errMsg = '['+mb+'] ['+searchIdx+']: Error trashing message: ' + err;
          cb(errMsg)
          return null;
        }
        log.info('[%s] [%s] Trashed %s messages', mb, searchIdx, numMessages) ;
      });

    })
  })

}

module.exports = Mailbox
<commit_msg>Only want the message.id back in the response. Rest is crud. Save the planet<commit_after>"use strict"

var
    cfg        = require('config'),
    GmailModel = require('gmail-model'),
    log4js     = require('log4js')

/*
 * Object variables
 */

var
    gmail,
    log,
    name,
    searches

/**
 * Mailbox model constructor.
 * @param {object}   params - Params to be passed in
 * @param {string}   params.clientSecretFile - full path to the client secret file to be used by google if an access token doesn't yet exist
 * @param {string[]} params.googleScopes - Google drive scopes for which this object instance will have permissions
 * @param {string}   params.name - Name of the mailbox (for logging)
 * @param {string}   params.tokenDir - directory on the local machine in which the google access token lives
 * @param {string}   params.tokenFile - name of file on the local machine that contains the google access token
 * @constructor
 */
function Mailbox (params) {

  // logs

  log4js.configure(cfg.log.log4jsConfigs);

  log = log4js.getLogger(cfg.log.appName);
  log.setLevel(cfg.log.level);

  // Setup the gmail inbox
  this.gmail = new GmailModel({
    clientSecretFile: params.clientSecretFile,
    googleScopes:     params.googleScopes,
    name:             params.name,
    tokenDir:         params.tokenDir,
    tokenFile:        params.tokenFile
  })

  this.name     = params.name
  this.searches = params.searches

  // Log out the searches
  var self = this
  this.searches.forEach( function(el, idx) {
    log.info('[%s] [%s] Search: ', self.name, idx, el)
  })

}

var method = Mailbox.prototype

/**
 * mailbox.getMessages
 *
 * @desc Idetify messages matching search criteria
 *
 * @alias mailbox.getMessages
 *
 * @param  {object} params - Parameters for request (currently unused)
 * @param  {callback} callback - The callback that handles the error response. Returns callback(errMsg)
 * @return {object} errMsg -
 */
method.process = function (params,cb) {

  var mb = this.name

  var self = this;

  self.searches.forEach( function (el, searchIdx) {

    log.info('[%s] [%s] Searching', self.name, searchIdx) ;

    self.gmail.listMessages({
      freetextSearch: el,
      retFields: ['messages(id)']
    }, function (err, messages) {

      if (err) {
        var errMsg = '['+mb+'] ['+searchIdx+']: Error retrieving emails for search: "'+err;
        cb(errMsg)
        return null;
      }

      var numMessages = messages.length
      log.info('[%s] [%s] Trashing %s messages', self.name, searchIdx, numMessages ) ;

      // Bail if there was nothing to delete
      if (messages.length == 0) return;

      var ids = [];
      messages.forEach( function(el,idx) {
        ids.push(el.id)
      })

      // Log out details of the message about to be trashed
      ids.forEach( function(el,idx) {

        // Get message details to improve logging
        self.gmail.getMessage({
          messageId: el,
          format: 'metadata',
          metadataHeaders: ['Subject', 'Date'],
          retFields: ['payload(headers)']
        }, function (err, resp) {

          if (err) {
            var errMsg = '['+mb+'] ['+searchIdx+']: Error getting details for message '+el+': '+err;
            cb(errMsg)
            return null;
          }

          // Get metadata for better logging
          var subject = ''
          var date    = ''
	  var headers = resp.payload.headers

          for (var i = 0; i < headers.length; i++) {
            if (headers[i].name == "Subject") { subject = headers[i].value }
            else if (headers[i].name == "Date") { date = headers[i].value }
          }

          log.info('[%s] [%s] Trashing message (%s) "%s" (%s)', self.name, searchIdx, el, subject, date) ;
	})
      })

      // Trash them
      self.gmail.trashMessages({
        messageIds: ids
      }, function (err,resps) {
        if (err) {
          var errMsg = '['+mb+'] ['+searchIdx+']: Error trashing message: ' + err;
          cb(errMsg)
          return null;
        }
        log.info('[%s] [%s] Trashed %s messages', mb, searchIdx, numMessages) ;
      });

    })
  })

}

module.exports = Mailbox
<|endoftext|>


<commit_before>function draw_body(id){
    // bodies orbit around parent
    bodies[id][6] += bodies[id][5];
    if(bodies[id][6] > 360){
        bodies[id][6] -= 360;
    }else if(bodies[id][6] < 0){
        bodies[id][6] += 360;
    }
    bodies[id][0] = bodies[id][4] * Math.cos(bodies[id][6]);
    bodies[id][1] = bodies[id][4] * Math.sin(bodies[id][6]);

    // draw body
    canvas.fillStyle = bodies[id][7];
    canvas.beginPath();
    canvas.arc(
      bodies[id][0],
      bodies[id][1],
      bodies[id][2],
      0,
      pi_times_two,
      1
    );
    canvas.closePath();
    canvas.fill();

    // draw orbit path and line to parent
    if(settings[0]
      || settings[1]){
        canvas.strokeStyle = bodies[id][7];
        canvas.lineWidth = Math.ceil(bodies[id][2] / 10) / zoom;

        canvas.beginPath();
        if(settings[0]){ // orbit path
            canvas.arc(
              0,
              0,
              bodies[id][4],
              0,
              pi_times_two,
              1
            );
        }
        if(settings[1]){ // line to parent
            canvas.moveTo(
              bodies[id][0],
              bodies[id][1]
            );
            canvas.lineTo(
              0,
              0
            );
        }
        canvas.closePath();
        canvas.stroke();
    }

    // draw moons
    if(bodies[id][8] != 0){
        var moonloop_counter = bodies[id][8].length - 1;
        if(moonloop_counter >= 0){
            do{
                bodies[id][8][moonloop_counter][6] += bodies[id][8][moonloop_counter][5];
                if(bodies[id][8][moonloop_counter][6] > 360){
                    bodies[id][8][moonloop_counter][6] -= 360;
                }else if(bodies[id][8][moonloop_counter][6] < 0){
                    bodies[id][8][moonloop_counter][6] += 360;
                }
                bodies[id][8][moonloop_counter][0] =
                  bodies[id][8][moonloop_counter][4]
                  * Math.cos(bodies[id][8][moonloop_counter][6])
                  + bodies[id][0];
                bodies[id][8][moonloop_counter][1] =
                  bodies[id][8][moonloop_counter][4]
                  * Math.sin(bodies[id][8][moonloop_counter][6])
                  + bodies[id][1];

                canvas.fillStyle = bodies[id][8][moonloop_counter][7];
                canvas.beginPath();
                canvas.arc(
                  bodies[id][8][moonloop_counter][0],
                  bodies[id][8][moonloop_counter][1],
                  bodies[id][8][moonloop_counter][2],
                  0,
                  pi_times_two,
                  1
                );
                canvas.closePath();
                canvas.fill();

                // draw orbit path and line to parent
                if(settings[0]
                  || settings[1]){
                    canvas.strokeStyle = bodies[id][8][moonloop_counter][7];
                    canvas.lineWidth = Math.ceil(bodies[id][8][moonloop_counter][2] / 10) / zoom;

                    canvas.beginPath();
                    if(settings[0]){ // orbit path
                        canvas.arc(
                          bodies[id][0],
                          bodies[id][1],
                          bodies[id][8][moonloop_counter][4],
                          0,
                          pi_times_two,
                          1
                        );
                    }
                    if(settings[1]){ // line to parent
                        canvas.moveTo(
                          bodies[id][0],
                          bodies[id][1]
                        );
                        canvas.lineTo(
                          bodies[id][8][moonloop_counter][0],
                          bodies[id][8][moonloop_counter][1]
                        );
                    }
                    canvas.closePath();
                    canvas.stroke();
                }
            }while(moonloop_counter--);
        }
    }
}

function draw(){
    if(key_down){
        camera_y -= 10 / zoom;
    }
    if(key_left){
        camera_x += 10 / zoom;
    }
    if(key_right){
        camera_x -= 10 / zoom;
    }
    if(key_up){
        camera_y += 10 / zoom;
    }

    canvas.clearRect(
      0,
      0,
      width,
      height
    );
    canvas.translate(
      x,
      y
    );
    canvas.scale(
      zoom,
      zoom
    );
    canvas.translate(
      camera_x,
      camera_y
    );

    var loop_counter = bodies.length - 1;
    if(loop_counter >= 0){
        do{
            draw_body(loop_counter);
        }while(loop_counter--);
    }

    canvas.fillStyle = solar[1];
    canvas.beginPath();
    canvas.arc(
      0,
      0,
      solar[0],
      0,
      pi_times_two,
      1
    );
    canvas.closePath();
    canvas.fill();

    canvas.translate(
      -camera_x,
      -camera_y
    );
    canvas.scale(
      1 / zoom,
      1 / zoom
    );
    canvas.translate(
      -x,
      -y
    );

    canvas.font = '23pt sans-serif';
    canvas.fillStyle = '#fff';
    canvas.fillText(
      'H,L,MouseWheel,O,WASD',
      0,
      23
    );
    canvas.fillText(
      zoom,
      0,
      50
    );
}

function generate_solarsystem(){
    bodies.length = 0;

    var bodyloop_counter = random_number(5) + 1;
    var moonloop_counter = 0;
    var radius = 0;
    do{
        radius = random_number(10) + 3;

        bodies.push([
          0,
          0,
          radius,
          radius,
          random_number(2323) + 232,
          Math.random() / 100,
          random_number(360),
          '#'
            + (random_number(5) + 4)
            + (random_number(5) + 4)
            + (random_number(5) + 4),
          0
        ]);

        // does body have moons?
        if(Math.random() > .5){
            bodies[bodies.length - 1][8] = [];

            moonloop_counter = random_number(2) + 1;
            do{
                radius = random_number(5) + 2;
                bodies[bodies.length - 1][8].push([
                  0,
                  0,
                  radius,
                  radius,
                  random_number(100) + 15,
                  (Math.random() - .5) / 5,
                  random_number(360),
                  '#'
                    + (random_number(5) + 4)
                    + (random_number(5) + 4)
                    + (random_number(5) + 4),
                  bodies.length - 1
                ]);
            }while(moonloop_counter--);
        }
    }while(bodyloop_counter--);

    solar = [
      // solar radius
      random_number(99) + 5,

      // solar color
      '#'
        + (random_number(4) + 5)
        + (random_number(4) + 5)
        + (random_number(4) + 5)
    ];
}

function mouse_wheel(e){
    zoom += (e.wheelDelta || -e.detail > 0) > 0
      ? .05
      : -.05;

    if(zoom < .1){
        zoom = .1;

    }else if(zoom > 3){
        zoom = 3;
    }

    zoom = parseFloat(zoom.toFixed(2));
}

function random_number(i){
    return Math.floor(Math.random() * i);
}

function resize(){
    height = window.innerHeight;
    document.getElementById('canvas').height = height;
    y = height / 2;

    width = window.innerWidth;
    document.getElementById('canvas').width = width;
    x = width / 2;
}

var bodies = [];
var camera_x = 0;
var camera_y = 0;
var canvas = document.getElementById('canvas').getContext('2d');
var height = 0;
var key_down = 0;
var key_left = 0;
var key_right = 0;
var key_up = 0;
var pi_times_two = Math.PI * 2;
var settings = [
  1,
  1
];
var solar = [];
var width = 0;
var x = 0;
var y = 0;
var zoom = 1;

generate_solarsystem();

window.onresize = resize;
resize();

if('onmousewheel' in window){
    window.onmousewheel = mouse_wheel;

}else{
    document.addEventListener(
      'DOMMouseScroll',
      mouse_wheel,
      false
    );
}

setInterval(
  'draw()',
  35
);

window.onkeydown = function(e){
    var key = window.event ? event : e;
    key = key.charCode ? key.charCode : key.keyCode;

    if(key === 65){ // A
        key_left = 1;

    }else if(key === 68){ // D
        key_right = 1;

    }else if(key === 83){ // S
        key_down = 1;

    }else if(key === 87){ // W
        key_up = 1;

    }else if(key === 76){ // L
        // lines to parent
        settings[1] = !settings[1];

    }else if(key === 79){ // O
        // orbit paths
        settings[0] = !settings[0];

    }else if(key === 72){ // H
        generate_solarsystem();
    }
};

window.onkeyup = function(e){
    var key = window.event ? event : e;
    key = key.charCode ? key.charCode : key.keyCode;

    if(key === 65){ // A
        key_left = 0;

    }else if(key === 68){ // D
        key_right = 0;

    }else if(key === 83){ // S
        key_down = 0;

    }else if(key === 87){ // W
        key_up = 0;
    }
};
<commit_msg>replaced settings array with settings object<commit_after>function draw_body(id){
    // bodies orbit around parent
    bodies[id][6] += bodies[id][5];
    if(bodies[id][6] > 360){
        bodies[id][6] -= 360;
    }else if(bodies[id][6] < 0){
        bodies[id][6] += 360;
    }
    bodies[id][0] = bodies[id][4] * Math.cos(bodies[id][6]);
    bodies[id][1] = bodies[id][4] * Math.sin(bodies[id][6]);

    // draw body
    canvas.fillStyle = bodies[id][7];
    canvas.beginPath();
    canvas.arc(
      bodies[id][0],
      bodies[id][1],
      bodies[id][2],
      0,
      pi_times_two,
      1
    );
    canvas.closePath();
    canvas.fill();

    // draw orbit path and line to parent
    if(settings['line-orbit']
      || settings['line-parent']){
        canvas.strokeStyle = bodies[id][7];
        canvas.lineWidth = Math.ceil(bodies[id][2] / 10) / zoom;

        canvas.beginPath();
        if(settings['line-orbit']){
            canvas.arc(
              0,
              0,
              bodies[id][4],
              0,
              pi_times_two,
              1
            );
        }
        if(settings['line-parent']){
            canvas.moveTo(
              bodies[id][0],
              bodies[id][1]
            );
            canvas.lineTo(
              0,
              0
            );
        }
        canvas.closePath();
        canvas.stroke();
    }

    // draw moons
    if(bodies[id][8] != 0){
        var moonloop_counter = bodies[id][8].length - 1;
        if(moonloop_counter >= 0){
            do{
                bodies[id][8][moonloop_counter][6] += bodies[id][8][moonloop_counter][5];
                if(bodies[id][8][moonloop_counter][6] > 360){
                    bodies[id][8][moonloop_counter][6] -= 360;
                }else if(bodies[id][8][moonloop_counter][6] < 0){
                    bodies[id][8][moonloop_counter][6] += 360;
                }
                bodies[id][8][moonloop_counter][0] =
                  bodies[id][8][moonloop_counter][4]
                  * Math.cos(bodies[id][8][moonloop_counter][6])
                  + bodies[id][0];
                bodies[id][8][moonloop_counter][1] =
                  bodies[id][8][moonloop_counter][4]
                  * Math.sin(bodies[id][8][moonloop_counter][6])
                  + bodies[id][1];

                canvas.fillStyle = bodies[id][8][moonloop_counter][7];
                canvas.beginPath();
                canvas.arc(
                  bodies[id][8][moonloop_counter][0],
                  bodies[id][8][moonloop_counter][1],
                  bodies[id][8][moonloop_counter][2],
                  0,
                  pi_times_two,
                  1
                );
                canvas.closePath();
                canvas.fill();

                // draw orbit path and line to parent
                if(settings['line-orbit']
                  || settings['line-parent']){
                    canvas.strokeStyle = bodies[id][8][moonloop_counter][7];
                    canvas.lineWidth = Math.ceil(bodies[id][8][moonloop_counter][2] / 10) / zoom;

                    canvas.beginPath();
                    if(settings['line-orbit']){
                        canvas.arc(
                          bodies[id][0],
                          bodies[id][1],
                          bodies[id][8][moonloop_counter][4],
                          0,
                          pi_times_two,
                          1
                        );
                    }
                    if(settings['line-parent']){
                        canvas.moveTo(
                          bodies[id][0],
                          bodies[id][1]
                        );
                        canvas.lineTo(
                          bodies[id][8][moonloop_counter][0],
                          bodies[id][8][moonloop_counter][1]
                        );
                    }
                    canvas.closePath();
                    canvas.stroke();
                }
            }while(moonloop_counter--);
        }
    }
}

function draw(){
    if(key_down){
        camera_y -= 10 / zoom;
    }
    if(key_left){
        camera_x += 10 / zoom;
    }
    if(key_right){
        camera_x -= 10 / zoom;
    }
    if(key_up){
        camera_y += 10 / zoom;
    }

    canvas.clearRect(
      0,
      0,
      width,
      height
    );
    canvas.translate(
      x,
      y
    );
    canvas.scale(
      zoom,
      zoom
    );
    canvas.translate(
      camera_x,
      camera_y
    );

    var loop_counter = bodies.length - 1;
    if(loop_counter >= 0){
        do{
            draw_body(loop_counter);
        }while(loop_counter--);
    }

    canvas.fillStyle = solar[1];
    canvas.beginPath();
    canvas.arc(
      0,
      0,
      solar[0],
      0,
      pi_times_two,
      1
    );
    canvas.closePath();
    canvas.fill();

    canvas.translate(
      -camera_x,
      -camera_y
    );
    canvas.scale(
      1 / zoom,
      1 / zoom
    );
    canvas.translate(
      -x,
      -y
    );

    canvas.font = '23pt sans-serif';
    canvas.fillStyle = '#fff';
    canvas.fillText(
      'H,L,MouseWheel,O,WASD',
      0,
      23
    );
    canvas.fillText(
      zoom,
      0,
      50
    );
}

function generate_solarsystem(){
    bodies.length = 0;

    var bodyloop_counter = random_number(5) + 1;
    var moonloop_counter = 0;
    var radius = 0;
    do{
        radius = random_number(10) + 3;

        bodies.push([
          0,
          0,
          radius,
          radius,
          random_number(2323) + 232,
          Math.random() / 100,
          random_number(360),
          '#'
            + (random_number(5) + 4)
            + (random_number(5) + 4)
            + (random_number(5) + 4),
          0
        ]);

        // does body have moons?
        if(Math.random() > .5){
            bodies[bodies.length - 1][8] = [];

            moonloop_counter = random_number(2) + 1;
            do{
                radius = random_number(5) + 2;
                bodies[bodies.length - 1][8].push([
                  0,
                  0,
                  radius,
                  radius,
                  random_number(100) + 15,
                  (Math.random() - .5) / 5,
                  random_number(360),
                  '#'
                    + (random_number(5) + 4)
                    + (random_number(5) + 4)
                    + (random_number(5) + 4),
                  bodies.length - 1
                ]);
            }while(moonloop_counter--);
        }
    }while(bodyloop_counter--);

    solar = [
      // solar radius
      random_number(99) + 5,

      // solar color
      '#'
        + (random_number(4) + 5)
        + (random_number(4) + 5)
        + (random_number(4) + 5)
    ];
}

function mouse_wheel(e){
    zoom += (e.wheelDelta || -e.detail > 0) > 0
      ? .05
      : -.05;

    if(zoom < .1){
        zoom = .1;

    }else if(zoom > 3){
        zoom = 3;
    }

    zoom = parseFloat(zoom.toFixed(2));
}

function random_number(i){
    return Math.floor(Math.random() * i);
}

function resize(){
    height = window.innerHeight;
    document.getElementById('canvas').height = height;
    y = height / 2;

    width = window.innerWidth;
    document.getElementById('canvas').width = width;
    x = width / 2;
}

var bodies = [];
var camera_x = 0;
var camera_y = 0;
var canvas = document.getElementById('canvas').getContext('2d');
var height = 0;
var key_down = 0;
var key_left = 0;
var key_right = 0;
var key_up = 0;
var pi_times_two = Math.PI * 2;
var settings = {
  'line-orbit' : true,
  'line-parent': true,
};
var solar = [];
var width = 0;
var x = 0;
var y = 0;
var zoom = 1;

generate_solarsystem();

window.onresize = resize;
resize();

if('onmousewheel' in window){
    window.onmousewheel = mouse_wheel;

}else{
    document.addEventListener(
      'DOMMouseScroll',
      mouse_wheel,
      false
    );
}

setInterval(
  'draw()',
  35
);

window.onkeydown = function(e){
    var key = window.event ? event : e;
    key = key.charCode ? key.charCode : key.keyCode;

    if(key === 65){ // A
        key_left = 1;

    }else if(key === 68){ // D
        key_right = 1;

    }else if(key === 83){ // S
        key_down = 1;

    }else if(key === 87){ // W
        key_up = 1;

    }else if(key === 76){ // L
        settings['line-parent'] = !settings['line-parent'];

    }else if(key === 79){ // O
        settings['line-orbit'] = !settings['line-orbit'];

    }else if(key === 72){ // H
        generate_solarsystem();
    }
};

window.onkeyup = function(e){
    var key = window.event ? event : e;
    key = key.charCode ? key.charCode : key.keyCode;

    if(key === 65){ // A
        key_left = 0;

    }else if(key === 68){ // D
        key_right = 0;

    }else if(key === 83){ // S
        key_down = 0;

    }else if(key === 87){ // W
        key_up = 0;
    }
};
<|endoftext|>