<commit_before>import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import classNames from 'classnames';

const MIN_CHILD_BOTTOM_MARGIN = 70;

const Scrollinator = (ComposedComponent) => (

  class extends Component {
    constructor(props) {
      super(props);

      this.state = {
        documentRect: null,
        componentRect: null,
        scrollinatorReady: false,
        childItemsProps: [],
      };

      this.handleScroll = this.handleScroll.bind(this);
      this.savePositions = this.savePositions.bind(this);
      this.getScrollProps = this.getScrollProps.bind(this);
      this.childItemsPropsHandler = this.childItemsPropsHandler.bind(this);
    }

    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
        this.savePositions();
    }

    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
    }

    savePositions() {
      const componentRectNode = findDOMNode(this);
      const componentRect = componentRectNode ? componentRectNode.getBoundingClientRect() : null;

      this.setState({
        ...this.state,
        componentRect,
        documentRect: document.documentElement.getBoundingClientRect(),
        scrollinatorReady: !!componentRect,
      });
    }

    handleScroll() {
      this.savePositions();
    }

    childItemsPropsHandler(scrollProps) {
      const { childItemsProps } = this.state;
      childItemsProps.push(scrollProps);
      this.setState({ ...this.state, childItemsProps });
    }

    getScrollProps() {
      const { documentRect, componentRect, scrollinatorReady, childItemsProps } = this.state;

      const scrollProps = {};

      if (componentRect) {
        // How scrolled down the page the document is
        scrollProps.scrollY = 0 - documentRect.top;
        // The Y scroll position of the top of the component
        scrollProps.componentTopY = componentRect.top;
        // The Y scroll position of the bottom of the component
        scrollProps.componentBottomY = componentRect.bottom;
        // The distance in pixels from top of the document to the the top of the component
        scrollProps.componentTop = componentRect.top - documentRect.top;
        // The height of the component
        scrollProps.componentHeight = componentRect.height;
        // The height of the viewport
        scrollProps.viewportHeight = window.innerHeight;

        // This function takes a viewport Y value and returns an object containing indicators such
        // as whether the target is inside the component and how far through the component it is.
        // targetY is a value indicating the number of pixels from the top of the viewport the
        // target is located.
        scrollProps.getTargetYInfo = (targetY) => {
          return {
            componentIsBelowTarget: scrollProps.componentTopY > targetY,
            // The number of pixels below the component top targetY is
            componentHeightTraversed: targetY - scrollProps.componentTopY,
          };
        };

        if (childItemsProps.length) {
          // If we have got the ScrollinatorChild properties sent back via the
          // childItemsPropsHandler callback then we can perform calculations based on the children
          // as well.
          // First get the height of each child (it is assumed each child has a uniform height)
          scrollProps.childHeight = childItemsProps[0].componentHeight;
          // The number of child items in the scroll animation
          scrollProps.numChildItems = childItemsProps.length;
        }

        // This function takes the targetY value and returns information about it relative to the
        // child components
        scrollProps.getScrollinatorChildInfo = (targetY) => {
          // The height of each child
          const childHeight = scrollProps.childHeight || 0;

          // The bottom margin of each child. This is based on the distance between targetY and
          // the bottom of the viewport. Will not go any smaller than MIN_CHILD_BOTTOM_MARGIN
          let childMarginBottom = childHeight ?
            Math.floor(scrollProps.viewportHeight - targetY - childHeight) :
            MIN_CHILD_BOTTOM_MARGIN;

          if (childMarginBottom < MIN_CHILD_BOTTOM_MARGIN) {
            childMarginBottom = MIN_CHILD_BOTTOM_MARGIN;
          }

          // The full height of each child component including margin
          const childFullHeight = childHeight + childMarginBottom;

          // The current Y position of the last child
          const lastChildY = scrollProps.componentTopY +
            (childFullHeight * (childItemsProps.length - 1));

          // Indicates if the target is below the top of the last child item Y
          const targetIsBelowLastChildY = targetY > lastChildY;

          // Evaluate non child related targetY relative info
          const targetYInfo = scrollProps.getTargetYInfo(targetY);

          // We consider the current child background image Y to be "pinned" to the targetY if the
          // target is inside any child item except for the last
          const childBgIsPinned =
            !targetYInfo.componentIsBelowTarget && !targetIsBelowLastChildY;

          let childBgY;

          if (childBgIsPinned) {
            // If background is considered pinned then the child bg image should be at the same Y
            // index as the target
            childBgY = targetY;
          } else if (targetIsBelowLastChildY) {
            // If the target is inside or below the last child item then the background should be
            // the same Y index as the top of the last child
            childBgY = lastChildY;
          } else {
            // The bg image should follow the top of the scrollinated component
            childBgY = scrollProps.componentTopY;
          }

          // Determine the activeChildIndex from the progress of the target through the scrollinated
          // component
          let activeChildIndex = 0

          if (targetYInfo.componentHeightTraversed > 0) {
            activeChildIndex = Math.floor(targetYInfo.componentHeightTraversed / childFullHeight)

            if (activeChildIndex > childItemsProps.length - 1) {
              activeChildIndex = childItemsProps.length - 1;
            }
          }

          // Determine the number of pixels below the active child the the target is
          const activeChildHeightTraversed =
            targetYInfo.componentHeightTraversed - childFullHeight * activeChildIndex;

          // Generate a number between 0 and 1 indicating how far through the active child the
          // target has currently traversed
          const activeChildTraversalRatio = activeChildHeightTraversed > 0 ?
            activeChildHeightTraversed / childFullHeight :
            0;

          return {
            childHeight,
            childMarginBottom,
            childFullHeight,
            lastChildY,
            targetIsBelowLastChildY,
            childBgIsPinned,
            childBgY,
            activeChildIndex,
            activeChildHeightTraversed,
            activeChildTraversalRatio,
            // A property that needs to be passed into every child component decorated as a
            // ScrollinatorChild
            childItemsPropsHandler: this.childItemsPropsHandler,
            // hand back the targetY also
            targetY,
          };

          return {};
        };
      }

      return scrollProps;
    }

    render() {
      const { scrollinatorReady, childItemsProps } = this.state;

      if (!scrollinatorReady) {
        return

;
      }

      const scrollProps = this.getScrollProps();

      let bgSection = null;

      if (childItemsProps.length) {
        // targetY is passed into each childItemsProps callback so we can determine its value by
        // inspecting the props representing the first child
        const { targetY } = childItemsProps[0];

        // Now we have targetY we can access all the calculations for the children items
        const { getScrollinatorChildInfo } = scrollProps;
        const {
          targetYScrollinatorChildInfo,
          childBgY,
          activeChildIndex,
          activeChildTraversalRatio
        } = getScrollinatorChildInfo(targetY);

        bgSection = (

            {
              childItemsProps.map((item, index) => {
                const childBgStyles = {
                  childBg: {
                    top: `${childBgY}px`,
                  }
                }

                if (index === activeChildIndex && index < (childItemsProps.length - 1)) {
                  childBgStyles.childBg.opacity = 1 - activeChildTraversalRatio;
                } else if (index === activeChildIndex + 1) {
                  childBgStyles.childBg.opacity = activeChildTraversalRatio;
                }

                return (

                          {
                            childItemsProps.map((_, dotIndex) => (
                              <div key={`child-bg-dot${dotIndex}`}
                                className={
                                  classNames(
                                    'child-bg-dot',
                                    { 'active': dotIndex === activeChildIndex }
                                  )
                                }
                                />
                            ))
                          }

                );
              })
            }

        );
      }

      return (

          <ComposedComponent
            {...this.props}
            {...scrollProps}
          />
          { bgSection }

      );
    }
  }

);

export default Scrollinator;
<commit_msg>Remove retina image file rename<commit_after>import React, { Component } from 'react';
import { findDOMNode } from 'react-dom';
import classNames from 'classnames';

const MIN_CHILD_BOTTOM_MARGIN = 70;

const Scrollinator = (ComposedComponent) => (

  class extends Component {
    constructor(props) {
      super(props);

      this.state = {
        documentRect: null,
        componentRect: null,
        scrollinatorReady: false,
        childItemsProps: [],
      };

      this.handleScroll = this.handleScroll.bind(this);
      this.savePositions = this.savePositions.bind(this);
      this.getScrollProps = this.getScrollProps.bind(this);
      this.childItemsPropsHandler = this.childItemsPropsHandler.bind(this);
    }

    componentDidMount() {
        window.addEventListener('scroll', this.handleScroll);
        this.savePositions();
    }

    componentWillUnmount() {
        window.removeEventListener('scroll', this.handleScroll);
    }

    savePositions() {
      const componentRectNode = findDOMNode(this);
      const componentRect = componentRectNode ? componentRectNode.getBoundingClientRect() : null;

      this.setState({
        ...this.state,
        componentRect,
        documentRect: document.documentElement.getBoundingClientRect(),
        scrollinatorReady: !!componentRect,
      });
    }

    handleScroll() {
      this.savePositions();
    }

    childItemsPropsHandler(scrollProps) {
      const { childItemsProps } = this.state;
      childItemsProps.push(scrollProps);
      this.setState({ ...this.state, childItemsProps });
    }

    getScrollProps() {
      const { documentRect, componentRect, scrollinatorReady, childItemsProps } = this.state;

      const scrollProps = {};

      if (componentRect) {
        // How scrolled down the page the document is
        scrollProps.scrollY = 0 - documentRect.top;
        // The Y scroll position of the top of the component
        scrollProps.componentTopY = componentRect.top;
        // The Y scroll position of the bottom of the component
        scrollProps.componentBottomY = componentRect.bottom;
        // The distance in pixels from top of the document to the the top of the component
        scrollProps.componentTop = componentRect.top - documentRect.top;
        // The height of the component
        scrollProps.componentHeight = componentRect.height;
        // The height of the viewport
        scrollProps.viewportHeight = window.innerHeight;

        // This function takes a viewport Y value and returns an object containing indicators such
        // as whether the target is inside the component and how far through the component it is.
        // targetY is a value indicating the number of pixels from the top of the viewport the
        // target is located.
        scrollProps.getTargetYInfo = (targetY) => {
          return {
            componentIsBelowTarget: scrollProps.componentTopY > targetY,
            // The number of pixels below the component top targetY is
            componentHeightTraversed: targetY - scrollProps.componentTopY,
          };
        };

        if (childItemsProps.length) {
          // If we have got the ScrollinatorChild properties sent back via the
          // childItemsPropsHandler callback then we can perform calculations based on the children
          // as well.
          // First get the height of each child (it is assumed each child has a uniform height)
          scrollProps.childHeight = childItemsProps[0].componentHeight;
          // The number of child items in the scroll animation
          scrollProps.numChildItems = childItemsProps.length;
        }

        // This function takes the targetY value and returns information about it relative to the
        // child components
        scrollProps.getScrollinatorChildInfo = (targetY) => {
          // The height of each child
          const childHeight = scrollProps.childHeight || 0;

          // The bottom margin of each child. This is based on the distance between targetY and
          // the bottom of the viewport. Will not go any smaller than MIN_CHILD_BOTTOM_MARGIN
          let childMarginBottom = childHeight ?
            Math.floor(scrollProps.viewportHeight - targetY - childHeight) :
            MIN_CHILD_BOTTOM_MARGIN;

          if (childMarginBottom < MIN_CHILD_BOTTOM_MARGIN) {
            childMarginBottom = MIN_CHILD_BOTTOM_MARGIN;
          }

          // The full height of each child component including margin
          const childFullHeight = childHeight + childMarginBottom;

          // The current Y position of the last child
          const lastChildY = scrollProps.componentTopY +
            (childFullHeight * (childItemsProps.length - 1));

          // Indicates if the target is below the top of the last child item Y
          const targetIsBelowLastChildY = targetY > lastChildY;

          // Evaluate non child related targetY relative info
          const targetYInfo = scrollProps.getTargetYInfo(targetY);

          // We consider the current child background image Y to be "pinned" to the targetY if the
          // target is inside any child item except for the last
          const childBgIsPinned =
            !targetYInfo.componentIsBelowTarget && !targetIsBelowLastChildY;

          let childBgY;

          if (childBgIsPinned) {
            // If background is considered pinned then the child bg image should be at the same Y
            // index as the target
            childBgY = targetY;
          } else if (targetIsBelowLastChildY) {
            // If the target is inside or below the last child item then the background should be
            // the same Y index as the top of the last child
            childBgY = lastChildY;
          } else {
            // The bg image should follow the top of the scrollinated component
            childBgY = scrollProps.componentTopY;
          }

          // Determine the activeChildIndex from the progress of the target through the scrollinated
          // component
          let activeChildIndex = 0

          if (targetYInfo.componentHeightTraversed > 0) {
            activeChildIndex = Math.floor(targetYInfo.componentHeightTraversed / childFullHeight)

            if (activeChildIndex > childItemsProps.length - 1) {
              activeChildIndex = childItemsProps.length - 1;
            }
          }

          // Determine the number of pixels below the active child the the target is
          const activeChildHeightTraversed =
            targetYInfo.componentHeightTraversed - childFullHeight * activeChildIndex;

          // Generate a number between 0 and 1 indicating how far through the active child the
          // target has currently traversed
          const activeChildTraversalRatio = activeChildHeightTraversed > 0 ?
            activeChildHeightTraversed / childFullHeight :
            0;

          return {
            childHeight,
            childMarginBottom,
            childFullHeight,
            lastChildY,
            targetIsBelowLastChildY,
            childBgIsPinned,
            childBgY,
            activeChildIndex,
            activeChildHeightTraversed,
            activeChildTraversalRatio,
            // A property that needs to be passed into every child component decorated as a
            // ScrollinatorChild
            childItemsPropsHandler: this.childItemsPropsHandler,
            // hand back the targetY also
            targetY,
          };

          return {};
        };
      }

      return scrollProps;
    }

    render() {
      const { scrollinatorReady, childItemsProps } = this.state;

      if (!scrollinatorReady) {
        return

;
      }

      const scrollProps = this.getScrollProps();

      let bgSection = null;

      if (childItemsProps.length) {
        // targetY is passed into each childItemsProps callback so we can determine its value by
        // inspecting the props representing the first child
        const { targetY } = childItemsProps[0];

        // Now we have targetY we can access all the calculations for the children items
        const { getScrollinatorChildInfo } = scrollProps;
        const {
          targetYScrollinatorChildInfo,
          childBgY,
          activeChildIndex,
          activeChildTraversalRatio
        } = getScrollinatorChildInfo(targetY);

        bgSection = (

            {
              childItemsProps.map((item, index) => {
                const childBgStyles = {
                  childBg: {
                    top: `${childBgY}px`,
                  }
                }

                if (index === activeChildIndex && index < (childItemsProps.length - 1)) {
                  childBgStyles.childBg.opacity = 1 - activeChildTraversalRatio;
                } else if (index === activeChildIndex + 1) {
                  childBgStyles.childBg.opacity = activeChildTraversalRatio;
                }

                return (

                          {
                            childItemsProps.map((_, dotIndex) => (
                              <div key={`child-bg-dot${dotIndex}`}
                                className={
                                  classNames(
                                    'child-bg-dot',
                                    { 'active': dotIndex === activeChildIndex }
                                  )
                                }
                                />
                            ))
                          }

                );
              })
            }

        );
      }

      return (

          <ComposedComponent
            {...this.props}
            {...scrollProps}
          />
          { bgSection }

      );
    }
  }

);

export default Scrollinator;
<|endoftext|>


<commit_before>/**
 * @param {Collection} dataCollection
 * @param {int} total
 * @param {Document[]} documents
 * @param {object} [aggregations]
 * @param {object} [searchArgs]
 * @param previous
 * @property {Collection} dataCollection
 * @property {number} fetchedDocument
 * @constructor
 */
function KuzzleSearchResult (dataCollection, total, documents, aggregations, searchArgs, previous) {
  Object.defineProperties(this, {
    // read-only properties
    dataCollection: {
      value: dataCollection,
    },
    total: {
      value: total,
      enumerable: true
    },
    documents: {
      value: documents,
      enumerable: true
    },
    aggregations: {
      value: aggregations || {},
      enumerable: true
    },
    searchArgs: {
      value: searchArgs || {},
      enumerable: true
    },
    // writable properties
    fetchedDocument: {
      value: previous instanceof KuzzleSearchResult ? documents.length + previous.fetchedDocument : documents.length,
      enumerable: true,
      writable: true
    }
  });

  // promisifying
  if (this.dataCollection.kuzzle.bluebird) {
    return this.dataCollection.kuzzle.bluebird.promisifyAll(this, {
      suffix: 'Promise',
      filter: function (name, func, target, passes) {
        var whitelist = ['next'];

        return passes && whitelist.indexOf(name) !== -1;
      }
    });
  }

  return this;
}

/**
 * @param {function} cb
 */
KuzzleSearchResult.prototype.next = function (cb) {
  var
    filters,
    options = Object.assign({}, this.searchArgs.options);

  options.previous = this;

  // retrieve next results with scroll if original search use it
  if (options.scrollId) {
    if (this.fetchedDocument >= this.total) {
      cb(null, null);
      return;
    }

    // from and size parameters are not valid for a scroll action
    if (options.from) {
      delete options.from;
    }

    if (options.size) {
      delete options.size;
    }

    this.dataCollection.scroll(
      options.scrollId,
      options,
      this.searchArgs.filters || {},
      function(error, newSearchResults) {
        handleNextSearchResults(error, newSearchResults, cb);
      }
    );

    return;
  }

  // retrieve next results with from/size if original search use it
  if (options.from !== undefined && options.size !== undefined) {
    filters = Object.assign({}, this.searchArgs.filters);

    // check if we need to do next request to fetch all matching documents
    options.from += options.size;

    if (options.from >= this.total) {
      cb(null, null);

      return;
    }

    this.dataCollection.search(
      filters,
      options,
      function(error, newSearchResults) {
        handleNextSearchResults(error, newSearchResults, cb);
      }
    );

    return;
  }

  cb(new Error('Unable to retrieve next results from search: missing scrollId or from/size params'));
};

/**
 * @param {Error} error
 * @param {KuzzleSearchResult} newSearchResults
 * @param {Function} cb
 */
function handleNextSearchResults (error, newSearchResults, cb) {
  if (error) {
    cb(error);
    return;
  }

  cb(null, newSearchResults);
}

module.exports = KuzzleSearchResult;
<commit_msg>create a callback to call a callback is useless<commit_after>/**
 * @param {Collection} dataCollection
 * @param {int} total
 * @param {Document[]} documents
 * @param {object} [aggregations]
 * @param {object} [searchArgs]
 * @param previous
 * @property {Collection} dataCollection
 * @property {number} fetchedDocument
 * @constructor
 */
function KuzzleSearchResult (dataCollection, total, documents, aggregations, searchArgs, previous) {
  Object.defineProperties(this, {
    // read-only properties
    dataCollection: {
      value: dataCollection,
    },
    total: {
      value: total,
      enumerable: true
    },
    documents: {
      value: documents,
      enumerable: true
    },
    aggregations: {
      value: aggregations || {},
      enumerable: true
    },
    searchArgs: {
      value: searchArgs || {},
      enumerable: true
    },
    // writable properties
    fetchedDocument: {
      value: previous instanceof KuzzleSearchResult ? documents.length + previous.fetchedDocument : documents.length,
      enumerable: true,
      writable: true
    }
  });

  // promisifying
  if (this.dataCollection.kuzzle.bluebird) {
    return this.dataCollection.kuzzle.bluebird.promisifyAll(this, {
      suffix: 'Promise',
      filter: function (name, func, target, passes) {
        var whitelist = ['next'];

        return passes && whitelist.indexOf(name) !== -1;
      }
    });
  }

  return this;
}

/**
 * @param {function} cb
 */
KuzzleSearchResult.prototype.next = function (cb) {
  var
    filters,
    options = Object.assign({}, this.searchArgs.options);

  options.previous = this;

  // retrieve next results with scroll if original search use it
  if (options.scrollId) {
    if (this.fetchedDocument >= this.total) {
      cb(null, null);
      return;
    }

    // from and size parameters are not valid for a scroll action
    if (options.from) {
      delete options.from;
    }

    if (options.size) {
      delete options.size;
    }

    this.dataCollection.scroll(options.scrollId, options, this.searchArgs.filters || {}, cb);

    return;
  }

  // retrieve next results with from/size if original search use it
  if (options.from !== undefined && options.size !== undefined) {
    filters = Object.assign({}, this.searchArgs.filters);

    // check if we need to do next request to fetch all matching documents
    options.from += options.size;

    if (options.from >= this.total) {
      cb(null, null);

      return;
    }

    this.dataCollection.search(filters, options, cb);

    return;
  }

  cb(new Error('Unable to retrieve next results from search: missing scrollId or from/size params'));
};

module.exports = KuzzleSearchResult;
<|endoftext|>


<commit_before>angular.module('anol.map')

/**
 * @ngdoc object
 * @name anol.map.ClusterSelectServiceProvider
 */
.provider('ClusterSelectService', [function() {

    var _clusterSelectOptions;

    this.setClusterSelectOptions = function(options) {
        _clusterSelectOptions = options;
    };

    this.$get = ['MapService', function(MapService) {

        var defaultClusterOptions = {
            selectCluster: true,
            pointRadius: 10,
            spiral: true,
            circleMaxObjects: 10,
            maxObjects: 20,
            animate: false,
            animationDuration: 500
        };

        var ClusterSelect = function(clusterSelectOptions) {
            this.clusterLayers = [];
            this.selectRevealedFeatureCallbacks = [];
            this.clusterSelectOptions = clusterSelectOptions;
            this.selectedClusterLayer = undefined;
        };

        ClusterSelect.prototype.registerSelectRevealedFeatureCallback = function(f) {
            this.selectRevealedFeatureCallbacks.push(f);
        };

        ClusterSelect.prototype.addLayer = function(layer) {
            var self = this;
            layer.olLayer.on('change:visible', function() {
                if(!layer.getVisible() && layer === self.selectedClusterLayer) {
                    self.selectClusterInteraction.clear();
                }
            });
            this.clusterLayers.push(layer);
        };

        ClusterSelect.prototype.layerByFeature = function(feature) {
            var self = this;
            var resultLayer;
            // TODO collect all anol.layer.Feature into a list
            angular.forEach(self.clusterLayers, function(layer) {
                if(angular.isDefined(resultLayer)) {
                    return;
                }
                if(layer.unclusteredSource.getFeatures().indexOf(feature) > -1) {
                    if(layer instanceof anol.layer.DynamicGeoJSON) {
                        if(feature.get('__layer__') === layer.name) {
                            resultLayer = layer;
                        }
                    } else {
                        resultLayer = layer;
                    }
                }
            });
            return resultLayer;
        };

        ClusterSelect.prototype.getControl = function(recreate) {
            var self = this;
            if(self.clusterLayers.length === 0) {
                return;
            }

            if(angular.isDefined(self.selectClusterControl) && recreate !== true) {
                return self.selectClusterControl;
            }

            var olClusterLayers = [];
            angular.forEach(self.clusterLayers, function(layer) {
                olClusterLayers.push(layer.olLayer);
            });

            var interactionOptions = $.extend({}, defaultClusterOptions, this.clusterSelectOptions, {
                layers: olClusterLayers,
                // for each revealed feature of selected cluster, this function is called
                featureStyle: function(revealedFeature, resolution) {
                    var style = new ol.style.Style();
                    // style link lines
                    if(revealedFeature.get('selectclusterlink') === true) {
                        style = new ol.style.Style({
                            stroke: new ol.style.Stroke({
                                color: '#f00',
                                width: 1
                            })
                        });
                    }
                    if(revealedFeature.get('selectclusterfeature') === true) {
                        var originalFeature = revealedFeature.get('features')[0];
                        var layer = self.layerByFeature(originalFeature);
                        var layerStyle = layer.olLayer.getStyle();

                        if(angular.isFunction(layerStyle)) {
                            layerStyle = layerStyle(originalFeature, resolution)[0];
                        }

                        style = layerStyle;
                    }

                    return [style];
                },
                style: function(clusterFeature, resolution) {
                    if(clusterFeature.get('features').length === 1) {
                        var layer = self.layerByFeature(clusterFeature.get('features')[0]);
                        var style = layer.olLayer.getStyle();
                        if(angular.isFunction(style)) {
                            style = style(clusterFeature, resolution);
                        }
                        if(angular.isArray(style)) {
                            return style;
                        }
                        return [style];
                    }
                    return [new ol.style.Style()];
                }
            });

            var selectedCluster;
            self.selectClusterInteraction = new ol.interaction.SelectCluster(interactionOptions);

            var changeCursorCondition = function(pixel) {
                return MapService.getMap().hasFeatureAtPixel(pixel, function(layer) {
                    var found = false;
                    if(self.selectClusterInteraction.overlayLayer_ === layer) {
                        MapService.getMap().forEachFeatureAtPixel(pixel, function(feature) {
                            if(found) {
                                return;
                            }
                            if(feature.get('selectclusterfeature')) {
                                found = true;
                            }
                        });
                    }
                    return found;
                });
            };

            MapService.addCursorPointerCondition(changeCursorCondition);

            self.selectClusterInteraction.on('select', function(a) {
                if(a.selected.length === 1) {
                    var revealedFeature = a.selected[0];
                    if(revealedFeature.get('features').length > interactionOptions.maxObjects) {
                        // zoom in when not all revealed features displayed
                        var view = MapService.getMap().getView();
                        view.setZoom(view.getZoom() + 1);
                        return;
                    }
                    if(revealedFeature.get('selectclusterfeature') === true) {
                        // revealedFeature selected. execute callbacks
                        var originalFeature = revealedFeature.get('features')[0];
                        var layer = self.layerByFeature(originalFeature);
                        angular.forEach(self.selectRevealedFeatureCallbacks, function(f) {
                            f(revealedFeature, originalFeature, layer);
                        });
                        return;
                    }
                    if(revealedFeature.get('features').length > 1) {
                        // cluster with multiple features selected. cluster open
                        if(selectedCluster !== undefined) {
                            selectedCluster.setStyle(null);
                        }
                        selectedCluster = revealedFeature;
                        selectedCluster.setStyle(new ol.style.Style());
                        MapService.addCursorPointerCondition(changeCursorCondition);
                        return;
                    }
                    if(revealedFeature.get('features').length === 1) {
                        // cluster with one feature selected. clear selectedCluster style
                        if(selectedCluster !== undefined) {
                            selectedCluster.setStyle(null);
                            selectedCluster = undefined;
                        }
                    }
                } else if(a.selected.length === 0 && angular.isDefined(selectedCluster)) {
                    // cluster closed
                    selectedCluster.setStyle(null);
                    selectedCluster = undefined;
                    MapService.removeCursorPointerCondition(changeCursorCondition);
                }
            });

            self.selectClusterInteraction.getFeatures().on('add', function(e) {
                var features = e.element.get('features');
                var layer = self.layerByFeature(features[0]);
                self.selectedClusterLayer = layer;
                if(angular.isFunction(layer.clusterOptions.onSelect)) {
                    layer.clusterOptions.onSelect(features);
                }
            });

            MapService.getMap().addInteraction(self.selectClusterInteraction);

            self.selectClusterControl = new anol.control.Control({
                subordinate: true,
                olControl: null,
                interactions: [self.selectClusterInteraction]
            });

            self.selectClusterControl.onDeactivate(function() {
                self.selectClusterInteraction.setActive(false);
                MapService.removeCursorPointerCondition(changeCursorCondition);
            });

            // control active by default
            MapService.addCursorPointerCondition(changeCursorCondition);

            return this.selectClusterControl;
        };

        return new ClusterSelect(_clusterSelectOptions);
    }];
}]);<commit_msg>Clear selected cluster always when layer cluster layer visibility changes<commit_after>angular.module('anol.map')

/**
 * @ngdoc object
 * @name anol.map.ClusterSelectServiceProvider
 */
.provider('ClusterSelectService', [function() {

    var _clusterSelectOptions;

    this.setClusterSelectOptions = function(options) {
        _clusterSelectOptions = options;
    };

    this.$get = ['MapService', function(MapService) {

        var defaultClusterOptions = {
            selectCluster: true,
            pointRadius: 10,
            spiral: true,
            circleMaxObjects: 10,
            maxObjects: 20,
            animate: false,
            animationDuration: 500
        };

        var ClusterSelect = function(clusterSelectOptions) {
            this.clusterLayers = [];
            this.selectRevealedFeatureCallbacks = [];
            this.clusterSelectOptions = clusterSelectOptions;
        };

        ClusterSelect.prototype.registerSelectRevealedFeatureCallback = function(f) {
            this.selectRevealedFeatureCallbacks.push(f);
        };

        ClusterSelect.prototype.handleLayerVisibleChange = function(e) {
            this.selectClusterInteraction.clear();
        };

        ClusterSelect.prototype.addLayer = function(layer) {
            layer.olLayer.on('change:visible', this.handleLayerVisibleChange, this);
            this.clusterLayers.push(layer);
        };

        ClusterSelect.prototype.layerByFeature = function(feature) {
            var self = this;
            var resultLayer;
            // TODO collect all anol.layer.Feature into a list
            angular.forEach(self.clusterLayers, function(layer) {
                if(angular.isDefined(resultLayer)) {
                    return;
                }
                if(layer.unclusteredSource.getFeatures().indexOf(feature) > -1) {
                    if(layer instanceof anol.layer.DynamicGeoJSON) {
                        if(feature.get('__layer__') === layer.name) {
                            resultLayer = layer;
                        }
                    } else {
                        resultLayer = layer;
                    }
                }
            });
            return resultLayer;
        };

        ClusterSelect.prototype.getControl = function(recreate) {
            var self = this;
            if(self.clusterLayers.length === 0) {
                return;
            }

            if(angular.isDefined(self.selectClusterControl) && recreate !== true) {
                return self.selectClusterControl;
            }

            var olClusterLayers = [];
            angular.forEach(self.clusterLayers, function(layer) {
                olClusterLayers.push(layer.olLayer);
            });

            var interactionOptions = $.extend({}, defaultClusterOptions, this.clusterSelectOptions, {
                layers: olClusterLayers,
                // for each revealed feature of selected cluster, this function is called
                featureStyle: function(revealedFeature, resolution) {
                    var style = new ol.style.Style();
                    // style link lines
                    if(revealedFeature.get('selectclusterlink') === true) {
                        style = new ol.style.Style({
                            stroke: new ol.style.Stroke({
                                color: '#f00',
                                width: 1
                            })
                        });
                    }
                    if(revealedFeature.get('selectclusterfeature') === true) {
                        var originalFeature = revealedFeature.get('features')[0];
                        var layer = self.layerByFeature(originalFeature);
                        var layerStyle = layer.olLayer.getStyle();

                        if(angular.isFunction(layerStyle)) {
                            layerStyle = layerStyle(originalFeature, resolution)[0];
                        }

                        style = layerStyle;
                    }

                    return [style];
                },
                style: function(clusterFeature, resolution) {
                    if(clusterFeature.get('features').length === 1) {
                        var layer = self.layerByFeature(clusterFeature.get('features')[0]);
                        var style = layer.olLayer.getStyle();
                        if(angular.isFunction(style)) {
                            style = style(clusterFeature, resolution);
                        }
                        if(angular.isArray(style)) {
                            return style;
                        }
                        return [style];
                    }
                    return [new ol.style.Style()];
                }
            });

            var selectedCluster;
            self.selectClusterInteraction = new ol.interaction.SelectCluster(interactionOptions);

            var changeCursorCondition = function(pixel) {
                return MapService.getMap().hasFeatureAtPixel(pixel, function(layer) {
                    var found = false;
                    if(self.selectClusterInteraction.overlayLayer_ === layer) {
                        MapService.getMap().forEachFeatureAtPixel(pixel, function(feature) {
                            if(found) {
                                return;
                            }
                            if(feature.get('selectclusterfeature')) {
                                found = true;
                            }
                        });
                    }
                    return found;
                });
            };

            MapService.addCursorPointerCondition(changeCursorCondition);

            self.selectClusterInteraction.on('select', function(a) {
                if(a.selected.length === 1) {
                    var revealedFeature = a.selected[0];
                    if(revealedFeature.get('features').length > interactionOptions.maxObjects) {
                        // zoom in when not all revealed features displayed
                        var view = MapService.getMap().getView();
                        view.setZoom(view.getZoom() + 1);
                        return;
                    }
                    if(revealedFeature.get('selectclusterfeature') === true) {
                        // revealedFeature selected. execute callbacks
                        var originalFeature = revealedFeature.get('features')[0];
                        var layer = self.layerByFeature(originalFeature);
                        angular.forEach(self.selectRevealedFeatureCallbacks, function(f) {
                            f(revealedFeature, originalFeature, layer);
                        });
                        return;
                    }
                    if(revealedFeature.get('features').length > 1) {
                        // cluster with multiple features selected. cluster open
                        if(selectedCluster !== undefined) {
                            selectedCluster.setStyle(null);
                        }
                        selectedCluster = revealedFeature;
                        selectedCluster.setStyle(new ol.style.Style());
                        MapService.addCursorPointerCondition(changeCursorCondition);
                        return;
                    }
                    if(revealedFeature.get('features').length === 1) {
                        // cluster with one feature selected. clear selectedCluster style
                        if(selectedCluster !== undefined) {
                            selectedCluster.setStyle(null);
                            selectedCluster = undefined;
                        }
                    }
                } else if(a.selected.length === 0 && angular.isDefined(selectedCluster)) {
                    // cluster closed
                    selectedCluster.setStyle(null);
                    selectedCluster = undefined;
                    MapService.removeCursorPointerCondition(changeCursorCondition);
                }
            });

            self.selectClusterInteraction.getFeatures().on('add', function(e) {
                var features = e.element.get('features');
                var layer = self.layerByFeature(features[0]);
                if(angular.isFunction(layer.clusterOptions.onSelect)) {
                    layer.clusterOptions.onSelect(features);
                }
            });

            MapService.getMap().addInteraction(self.selectClusterInteraction);

            self.selectClusterControl = new anol.control.Control({
                subordinate: true,
                olControl: null,
                interactions: [self.selectClusterInteraction]
            });

            self.selectClusterControl.onDeactivate(function() {
                self.selectClusterInteraction.setActive(false);
                MapService.removeCursorPointerCondition(changeCursorCondition);
            });

            // control active by default
            MapService.addCursorPointerCondition(changeCursorCondition);

            return this.selectClusterControl;
        };

        return new ClusterSelect(_clusterSelectOptions);
    }];
}]);<|endoftext|>


<commit_before>var n2kMappings = require("./n2kMappings.js").mappings;
var through = require('through');

var toFlat = function (n2k) {
    return {
      updates: [
        {
          source: {
            pgn: n2k.pgn,
            timestamp: n2k.timestamp,
            src: n2k.src
          },
          values : toValuesArray(n2k)
        }
      ]
    }
}

var toValuesArray = function (n2k) {
  var theMappings = n2kMappings[n2k.pgn];
  if (typeof theMappings != 'undefined') {
    return theMappings
      .filter(function (theMapping) {
        try {
          return typeof theMapping.filter === 'undefined' || theMapping.filter(n2k);
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
          return false;
        }
      })
      .map(function (theMapping) {
        try {
          return  {
            path: theMapping.node,
            value: typeof theMapping.source != 'undefined' ?
              Number(n2k.fields[theMapping.source]) :
              theMapping.value(n2k),
            source: {
              pgn: n2k.pgn,
              timestamp: n2k.timestamp,
              src: n2k.src
            }
          }
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
        }
      })
      .filter(function (x) {
        return x != undefined;
      });
  }
  return [];
}

var flatToNested = function (msg) {
  var result = {};
  var temp = result;
  var parts = msg.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    temp[parts[i]] = {};
    temp = temp[parts[i]];
  }
  temp[parts[parts.length - 1]] = msg;
  msg.path = undefined;
  return result;
}

exports.toFlat = toFlat;
exports.toNested = function (n2k) {
  return toFlat(n2k).map(flatToNested);
}

exports.toFlatTransformer = function (options) {
  var stream = through(function (data) {
    if (options.debug) {
      console.log(data);
    }
    stream.queue(exports.toFlat(data));
  });
  return stream;
}

exports.toNestedTransformer = function (options) {
  var stream = through(function (data) {
    if (options.debug) {
      console.log(data);
    }
    exports.toNested(data).forEach(stream.queue);
  });
  return stream;
}<commit_msg>Fix nested output<commit_after>var n2kMappings = require("./n2kMappings.js").mappings;
var through = require('through');

var toFlat = function (n2k) {
    return {
      updates: [
        {
          source: {
            pgn: n2k.pgn,
            timestamp: n2k.timestamp,
            src: n2k.src
          },
          values : toValuesArray(n2k)
        }
      ]
    }
}

var toValuesArray = function (n2k) {
  var theMappings = n2kMappings[n2k.pgn];
  if (typeof theMappings != 'undefined') {
    return theMappings
      .filter(function (theMapping) {
        try {
          return typeof theMapping.filter === 'undefined' || theMapping.filter(n2k);
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
          return false;
        }
      })
      .map(function (theMapping) {
        try {
          return  {
            path: theMapping.node,
            value: typeof theMapping.source != 'undefined' ?
              Number(n2k.fields[theMapping.source]) :
              theMapping.value(n2k),
            source: {
              pgn: n2k.pgn,
              timestamp: n2k.timestamp,
              src: n2k.src
            }
          }
        } catch (ex) {
          process.stderr.write(ex + ' ' + n2k);
        }
      })
      .filter(function (x) {
        return x != undefined;
      });
  }
  return [];
}

var addToTree = function (pathValue, source, tree) {
  var result = {};
  var temp = tree;
  var parts = msg.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    temp[parts[i]] = {};
    temp = temp[parts[i]];
  }
  temp[parts[parts.length - 1]] = msg;
  return result;
}

function addAsNested(pathValue, source, result) {
  var temp = result;
  var parts = pathValue.path.split('.');
  for (var i = 0; i < parts.length - 1; i++) {
    if (typeof temp[parts[i]] === 'undefined') {
      temp[parts[i]] = {};
    }
    temp = temp[parts[i]];
  }
  temp[parts[parts.length - 1]] = {
    value: pathValue.value,
    source: source
  }
}

function deltaToNested(delta) {
  var result = {};
  delta.updates[0].values.forEach(function(pathValue) {
    addAsNested(pathValue, delta.updates[0].source, result);
  });
  return result;
}

exports.toFlat = toFlat;
exports.toNested = function (n2k) {
  return deltaToNested(toFlat(n2k));
}

exports.toFlatTransformer = function (options) {
  var stream = through(function (data) {
    if (options.debug) {
      console.log(data);
    }
    stream.queue(exports.toFlat(data));
  });
  return stream;
}

exports.toNestedTransformer = function (options) {
  var stream = through(function (data) {
    if (options.debug) {
      console.log(data);
    }
    var nested = exports.toNested(data);
    if (Object.getOwnPropertyNames(nested).length > 0) {
      stream.queue(nested);
    }
  });
  return stream;
}<|endoftext|>


<commit_before>// https://raw.githubusercontent.com/likr/d3-downloadable/master/src/index.js

export default class SvgConverter {

    constructor(sourceNode) {
        this.sourceNode = sourceNode;
    }

    toSvgData() {
        let htmlText = this.sourceNode.outerHTML;
        let base64EncodedText = window.btoa(
            window.encodeURIComponent(htmlText).replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode('0x' + p1)));

        return new Promise(resolve => {
            resolve('data:image/svg+xml;charset=utf-8;base64,' + base64EncodedText);
        });
    }

    toPngData() {
        return new Promise(resolve => {
            this.toSvgData().then(svgdata => {
                let {width, height} = this.sourceNode.getBoundingClientRect();
                let canvasNode = document.createElement('canvas');
                canvasNode.width = width;
                canvasNode.height = height;

                let graphicsContext = canvasNode.getContext('2d');

                let imageNode = new window.Image();
                imageNode.onload = () => {
                    graphicsContext.drawImage(imageNode, 0, 0);
                    resolve(canvasNode.toDataURL('image/png'));
                };
                imageNode.src = svgdata;
            });
        });
    }

    toJpegData() {
        return new Promise(resolve => {
            this.toSvgData().then(svgdata => {
                let {width, height} = this.sourceNode.getBoundingClientRect();
                let canvasNode = document.createElement('canvas');
                canvasNode.width = width;
                canvasNode.height = height;

                let graphicsContext = canvasNode.getContext('2d');

                let imageNode = new window.Image();
                imageNode.onload = () => {
                    graphicsContext.drawImage(imageNode, 0, 0);
                    resolve(canvasNode.toDataURL('image/jpeg'));
                };
                imageNode.src = svgdata;
            });
        });
    }
}
<commit_msg>Add converter from image url to data url<commit_after>// https://raw.githubusercontent.com/likr/d3-downloadable/master/src/index.js

export default class SvgConverter {

    constructor(sourceNode) {
        this.sourceNode = sourceNode;
    }

    toSvgData() {
        let htmlText = this.sourceNode.outerHTML;
        let base64EncodedText = window.btoa(
            window.encodeURIComponent(htmlText).replace(/%([0-9A-F]{2})/g, (match, p1) => String.fromCharCode('0x' + p1)));

        return new Promise(resolve => {
            resolve('data:image/svg+xml;charset=utf-8;base64,' + base64EncodedText);
        });
    }

    toPngData() {
        return new Promise(resolve => {
            this.toSvgData().then(svgdata => {
                let {width, height} = this.sourceNode.getBoundingClientRect();
                let canvasNode = document.createElement('canvas');
                canvasNode.width = width;
                canvasNode.height = height;

                let graphicsContext = canvasNode.getContext('2d');

                let imageNode = new window.Image();
                imageNode.onload = () => {
                    graphicsContext.drawImage(imageNode, 0, 0);
                    resolve(canvasNode.toDataURL('image/png'));
                };
                imageNode.src = svgdata;
            });
        });
    }

    toJpegData() {
        return new Promise(resolve => {
            this.toSvgData().then(svgdata => {
                let {width, height} = this.sourceNode.getBoundingClientRect();
                let canvasNode = document.createElement('canvas');
                canvasNode.width = width;
                canvasNode.height = height;

                let graphicsContext = canvasNode.getContext('2d');

                let imageNode = new window.Image();
                imageNode.onload = () => {
                    graphicsContext.drawImage(imageNode, 0, 0);
                    resolve(canvasNode.toDataURL('image/jpeg'));
                };
                imageNode.src = svgdata;
            });
        });
    }

    static fromImageUrl(imageUrl, imageType) {
        return new Promise(resolve => {
            let imageNode = new window.Image();
            imageNode.onload = () => {
                let canvasNode = document.createElement('canvas');
                canvasNode.width = imageNode.width;
                canvasNode.height = imageNode.height;

                let graphicsContext = canvasNode.getContext('2d');
                graphicsContext.drawImage(imageNode, 0, 0);

                resolve(canvasNode.toDataURL('image/' + imageType));
            };
            imageNode.crossOrigin = 'anonymous';
            imageNode.src = imageUrl;
        });
    }
}
<|endoftext|>


<commit_before>/** @module TdClassNames */

import * as Utils from './Utils';

/**
 * Adds highlight-min-max and one of highlight-min, highlight-max to the
 * cells that match min and max in the summary
 *
 * @param {Object} column The column definition
 * @param {Array} tableData the data for the whole table
 * @param {Array} columns The definitions of columns for the whole table
 *
 * @return {String} classnames
 */
export function minMaxClassName(cellData, summary, column, rowData) {
  const sortValue = Utils.getSortValueFromCellData(cellData, column, rowData);
  if (sortValue === summary.min) {
    return 'highlight-min-max highlight-min';
  } else if (sortValue === summary.max) {
    return 'highlight-min-max highlight-max';
  }

  return undefined;
}

/**
 * Adds highlight-min to the cells that match min in the summary.
 *
 * @param {Object} column The column definition
 * @param {Array} tableData the data for the whole table
 * @param {Array} columns The definitions of columns for the whole table
 *
 * @return {String} classnames
 */
export function minClassName(cellData, summary, column, rowData) {
  const sortValue = Utils.getSortValueFromCellData(cellData, column, rowData);
  if (sortValue === summary.min) {
    return 'highlight-min';
  }

  return undefined;
}

/**
 * Adds highlight-max to the cells that match max in the summary.
 *
 * @param {Object} column The column definition
 * @param {Array} tableData the data for the whole table
 * @param {Array} columns The definitions of columns for the whole table
 *
 * @return {String} classnames
 */
export function maxClassName(cellData, summary, column, rowData) {
  const sortValue = Utils.getSortValueFromCellData(cellData, column, rowData);
  if (sortValue === summary.max) {
    return 'highlight-max';
  }

  return undefined;
}

<commit_msg>Improve TdClassNames docs<commit_after>/**
 * TdClassNames is a collection of utility functions that can be used
 * in column definitions as the `tdClassName` attribute.
 *
 * These functions typically combine cell data and summary information
 * to produce a meaningful class name.
 *
 * They can take the following arguments:
 *  - `cellData` {Object} The data for the cell
 *  - `columnSummary` {Object} The summary for the column
 *  - `column` {Object} The definition for the column
 *  - `rowData` {Object} The data for the row
 *  - `highlightedColumn` {Boolean} true if this column is highlighted
 *  - `highlightedRow` {Boolean} true if this row is highlighted
 *  - `rowNumber` {Number} The row number
 *  - `tableData` {Array} the data for the whole table
 *  - `columns` {Array} the definitions for all columns
 *
 * @module TdClassNames
 */

import * as Utils from './Utils';

/**
 * Adds `highlight-min-max` and one of `highlight-min`, `highlight-max` to the
 * cells that match min and max in the summary
 *
 * @param {Any} cellData the data for the cell
 * @param {Object} columnSummary The column summary
 * @param {Object} column The column definition
 * @param {Array} rowData the data for the row
 *
 * @return {String} classnames
 */
export function minMaxClassName(cellData, columnSummary, column, rowData) {
  const sortValue = Utils.getSortValueFromCellData(cellData, column, rowData);
  if (sortValue === columnSummary.min) {
    return 'highlight-min-max highlight-min';
  } else if (sortValue === columnSummary.max) {
    return 'highlight-min-max highlight-max';
  }

  return undefined;
}

/**
 * Adds `highlight-min` to the cells that match min in the summary.
 *
 * @param {Any} cellData the data for the cell
 * @param {Object} columnSummary The column summary
 * @param {Object} column The column definition
 * @param {Array} rowData the data for the row
 *
 * @return {String} classnames
 */
export function minClassName(cellData, columnSummary, column, rowData) {
  const sortValue = Utils.getSortValueFromCellData(cellData, column, rowData);
  if (sortValue === columnSummary.min) {
    return 'highlight-min';
  }

  return undefined;
}

/**
 * Adds `highlight-max` to the cells that match max in the summary.
 *
 * @param {Any} cellData the data for the cell
 * @param {Object} columnSummary The column summary
 * @param {Object} column The column definition
 * @param {Array} rowData the data for the row
 *
 * @return {String} classnames
 */
export function maxClassName(cellData, columnSummary, column, rowData) {
  const sortValue = Utils.getSortValueFromCellData(cellData, column, rowData);
  if (sortValue === columnSummary.max) {
    return 'highlight-max';
  }

  return undefined;
}

<|endoftext|>


<commit_before>(function (env){
    "use strict";
    env.ddg_spice_njt = function(api_result) {
        if (!api_result || api_result.failed){
            return Spice.failed('njt');
        }

        var now = timeInMins(api_result.now);

        Spice.add({
            id: 'njt',
            name: 'NJ Transit',
            data: api_result.routes,
            meta: {
                heading: api_result.origin + " to " + api_result.destination,
                sourceUrl: api_result.url,
                sourceName: "NJ Transit"
            },
            templates: {
                group: 'base',
                detail: false,
                item_detail: false,
                options: {
                    content: Spice.njt.train_item
                }
            },
            normalize: function(item) {
                var classes = {
                        "Cancelled": "njt__cancelled",
                        "Delayed": "njt__delayed",
                        "All Aboard": "njt__boarding",
                        "Boarding": "njt__boarding",
                        "Stand By": "njt__boarding"
                    };

                if (!item.status) item.status = "On Time";
                return {
                    departure_time: format_time(actualDepartureTime(item, now)),
                    arrival_time: format_time(actualArrivalTime(item, now)),
                    status: actualStatus(item, now),
                    line: item.line.replace('Line', ''),
                    cancelled: item.status === "Cancelled",
                    status_class: delayedMins(item, now) > 0 ? "njt__delayed" : classes[item.status],
                    url: 'http://dv.njtransit.com/mobile/train_stops.aspx?train=' + item.train
                };
            },
            sort_fields: {
                time: function(a, b) {
                    var c = actualDepartureTime(a, now),
                        d = actualDepartureTime(b, now);
                    c = (c + 5 < now) ? c + 24*60 : c; //push times from the next day to the end of the list
                    d = (d + 5 < now) ? d + 24*60 : d;
                    return c - d;
                }
            },
            sort_default: 'time',
            onShow: function(){
                $('.njt__cancelled').parents('.tile__body').addClass('njt__cancelled-tile');
            }
        });
    };

    var delayed_regex = new RegExp(/In (\d+) Min/);

    function padZeros(n, len) {
        var s = n.toString();
        while (s.length < len) {
            s = '0' + s;
        }
        return s;
    }

    //takes a time in minutes (integer) and converts it to human-readable (string like 2:05 PM)
    function format_time(t) {
        var hour = Math.floor(t / 60),
            minute = t % 60,
            ampm = (hour >= 24) ? 'AM' : (hour >= 12) ? 'PM' : 'AM';
        if (hour > 24) {
            hour -= 24;
        } else if (hour > 12) {
            hour -= 12;
        }
        if (hour === 0){
            hour = 12;
        }
        return hour + ':' + padZeros(minute, 2) + ' ' + ampm;
    }

    //converts a time string (like 14:05 or 2:05 PM) to integer minutes
    function timeInMins(t) {
        var hour = parseInt(t.split(':')[0]),
            minute = parseInt(t.split(':')[1]),
            ampm = 0;
        if (t.indexOf('PM') > -1 && hour !== 12) {
            ampm = 60*12;
        } else if (t.indexOf('AM') > -1 && hour === 12) {
            ampm = -60*12;
        }
        return hour*60 + minute + ampm;
    }

    //find how many minutes the train is delayed
    function delayedMins(item, now) {
        var match = delayed_regex.exec(item.status);
        if (match && match.length > 1){
            var mins = parseInt(match[1]);
            return (now + mins) - timeInMins(item.departure_time);
        }
        return 0;
    }

    //departure time adjusted for delay
    function actualDepartureTime(item, now) {
        return timeInMins(item.departure_time) + delayedMins(item, now);
    }

    //arrival time adjusted for delay
    function actualArrivalTime(item, now) {
        return timeInMins(item.arrival_time) + delayedMins(item, now);
    }

    //status adjusted for delay
    function actualStatus(item, now) {
        if (delayedMins(item, now) > 0) { //if it's delayed, tell the user the scheduled departure time
            return 'Delayed from ' + format_time(timeInMins(item.departure_time));
        }
        if (item.status === "Boarding") {
            return 'Now Boarding';
        }
        var match = delayed_regex.exec(item.status);
        if (match && match.length > 1) { //if we know when it departs, tell the user how many minutes
            return 'Departs in ' + match[1] + 'm';
        }
        return item.status;
    }
}(this));
<commit_msg>NJT: fix sorting for late trains at the station<commit_after>(function (env){
    "use strict";
    env.ddg_spice_njt = function(api_result) {
        if (!api_result || api_result.failed){
            return Spice.failed('njt');
        }

        var now = timeInMins(api_result.now);

        Spice.add({
            id: 'njt',
            name: 'NJ Transit',
            data: api_result.routes,
            meta: {
                heading: api_result.origin + " to " + api_result.destination,
                sourceUrl: api_result.url,
                sourceName: "NJ Transit"
            },
            templates: {
                group: 'base',
                detail: false,
                item_detail: false,
                options: {
                    content: Spice.njt.train_item
                }
            },
            normalize: function(item) {
                var classes = {
                        "Cancelled": "njt__cancelled",
                        "Delayed": "njt__delayed",
                        "All Aboard": "njt__boarding",
                        "Boarding": "njt__boarding",
                        "Stand By": "njt__boarding"
                    };

                if (!item.status) item.status = "On Time";
                return {
                    departure_time: format_time(actualDepartureTime(item, now)),
                    arrival_time: format_time(actualArrivalTime(item, now)),
                    status: actualStatus(item, now),
                    line: item.line.replace('Line', ''),
                    cancelled: item.status === "Cancelled",
                    status_class: delayedMins(item, now) > 0 ? "njt__delayed" : classes[item.status],
                    url: 'http://dv.njtransit.com/mobile/train_stops.aspx?train=' + item.train
                };
            },
            sort_fields: {
                time: function(a, b) {
                    var c = actualDepartureTime(a, now),
                        d = actualDepartureTime(b, now),
                        boarding_statuses = ['All Aboard', 'Boarding', 'Stand By'];
                    c = (c + 5 < now) ? c + 24*60 : c; //push times from the next day to the end of the list
                    d = (d + 5 < now) ? d + 24*60 : d;
                    //when a delayed train gets to the station, the scheduled time is displayed,
                    //so normally it would go at the end of the list (because it looks like it is from the next day).
                    //of course, we want boarding trains to go to the front.
                    if (boarding_statuses.indexOf(a.status) > -1){
                        return -1;
                    } else if (boarding_statuses.indexOf(b.status) > -1){
                        return 1;
                    }
                    return c - d;
                }
            },
            sort_default: 'time',
            onShow: function(){
                $('.njt__cancelled').parents('.tile__body').addClass('njt__cancelled-tile');
            }
        });
    };

    var delayed_regex = new RegExp(/In (\d+) Min/);

    function padZeros(n, len) {
        var s = n.toString();
        while (s.length < len) {
            s = '0' + s;
        }
        return s;
    }

    //takes a time in minutes (integer) and converts it to human-readable (string like 2:05 PM)
    function format_time(t) {
        var hour = Math.floor(t / 60),
            minute = t % 60,
            ampm = (hour >= 24) ? 'AM' : (hour >= 12) ? 'PM' : 'AM';
        if (hour > 24) {
            hour -= 24;
        } else if (hour > 12) {
            hour -= 12;
        }
        if (hour === 0){
            hour = 12;
        }
        return hour + ':' + padZeros(minute, 2) + ' ' + ampm;
    }

    //converts a time string (like 14:05 or 2:05 PM) to integer minutes
    function timeInMins(t) {
        var hour = parseInt(t.split(':')[0]),
            minute = parseInt(t.split(':')[1]),
            ampm = 0;
        if (t.indexOf('PM') > -1 && hour !== 12) {
            ampm = 60*12;
        } else if (t.indexOf('AM') > -1 && hour === 12) {
            ampm = -60*12;
        }
        return hour*60 + minute + ampm;
    }

    //find how many minutes the train is delayed
    function delayedMins(item, now) {
        var match = delayed_regex.exec(item.status);
        if (match && match.length > 1){
            var mins = parseInt(match[1]);
            return (now + mins) - timeInMins(item.departure_time);
        }
        return 0;
    }

    //departure time adjusted for delay
    function actualDepartureTime(item, now) {
        return timeInMins(item.departure_time) + delayedMins(item, now);
    }

    //arrival time adjusted for delay
    function actualArrivalTime(item, now) {
        return timeInMins(item.arrival_time) + delayedMins(item, now);
    }

    //status adjusted for delay
    function actualStatus(item, now) {
        if (delayedMins(item, now) > 0) { //if it's delayed, tell the user the scheduled departure time
            return 'Delayed from ' + format_time(timeInMins(item.departure_time));
        }
        if (item.status === "Boarding") {
            return 'Now Boarding';
        }
        var match = delayed_regex.exec(item.status);
        if (match && match.length > 1) { //if we know when it departs, tell the user how many minutes
            return 'Departs in ' + match[1] + 'm';
        }
        return item.status;
    }
}(this));
<|endoftext|>


<commit_before>// @flow
import logger from '../logger'
import * as PushGen from './push-gen'
import * as ChatTypes from '../constants/types/rpc-chat-gen'
import * as Saga from '../util/saga'
import * as RPCTypes from '../constants/types/rpc-gen'
import {isMobile} from '../constants/platform'
import {chatTab} from '../constants/tabs'
import {navigateTo} from './route-tree'
import {createShowUserProfile} from './profile-gen'
import {
  requestPushPermissions,
  configurePush,
  displayNewMessageNotification,
  clearAllNotifications,
  setNoPushPermissions,
} from './platform-specific'

import type {TypedState} from '../constants/reducer'

const pushSelector = ({push: {token, tokenType}}: TypedState) => ({token, tokenType})
const deviceIDSelector = ({config: {deviceID}}: TypedState) => deviceID

function permissionsNoSaga() {
  return Saga.sequentially([
    Saga.call(setNoPushPermissions),
    Saga.put(PushGen.createPermissionsRequesting({requesting: false})),
    Saga.put(PushGen.createPermissionsPrompt({prompt: false})),
  ])
}

function* permissionsRequestSaga(): Saga.SagaGenerator<any, any> {
  try {
    yield Saga.put(PushGen.createPermissionsRequesting({requesting: true}))

    logger.info('Requesting permissions')
    const permissions = yield Saga.call(requestPushPermissions)
    logger.info('Permissions:', permissions)
    // TODO(gabriel): Set permissions we have in store, might want it at some point?
  } finally {
    yield Saga.put(PushGen.createPermissionsRequesting({requesting: false}))
    yield Saga.put(PushGen.createPermissionsPrompt({prompt: false}))
  }
}

function* pushNotificationSaga(notification: PushGen.NotificationPayload): Saga.SagaGenerator<any, any> {
  logger.info('Push notification:', notification)
  const payload = notification.payload.notification
  if (payload) {
    // Handle types that are not from user interaction
    if (payload.type === 'chat.newmessageSilent') {
      logger.info('Push notification: silent notification received')
      try {
        const unboxRes = yield Saga.call(ChatTypes.localUnboxMobilePushNotificationRpcPromise, {
          convID: payload.c || '',
          // $FlowIssue payload.t isn't ConversationMembersType
          membersType: typeof payload.t === 'string' ? parseInt(payload.t) : payload.t,
          payload: payload.m || '',
          pushIDs: typeof payload.p === 'string' ? JSON.parse(payload.p) : payload.p,
        })
        if (payload.x && payload.x > 0) {
          const num = payload.x
          const ageMS = Date.now() - num * 1000
          if (ageMS > 15000) {
            logger.info('Push notification: silent notification is stale:', ageMS)
            return
          }
        }
        if (unboxRes) {
          yield Saga.call(displayNewMessageNotification, unboxRes, payload.c, payload.b, payload.d)
        }
      } catch (err) {
        logger.info('failed to unbox silent notification', err)
      }
    } else if (payload.type === 'chat.readmessage') {
      logger.info('Push notification: read message notification received')
      const b = typeof payload.b === 'string' ? parseInt(payload.b) : payload.b
      if (b === 0) {
        clearAllNotifications()
      }
    }

    // Handle types from user interaction
    if (payload.userInteraction) {
      if (payload.type === 'chat.newmessage') {
        const {convID} = payload
        // Check for conversation ID so we know where to navigate to
        if (!convID) {
          logger.error('Push chat notification payload missing conversation ID')
          return
        }
        yield Saga.put(navigateTo([chatTab, convID]))
      } else if (payload.type === 'follow') {
        const {username} = payload
        if (!username) {
          logger.error('Follow notification payload missing username', JSON.stringify(payload))
          return
        }
        logger.info('Push notification: follow received, follower= ', username)
        yield Saga.put(createShowUserProfile({username}))
      } else {
        logger.error('Push notification payload missing or unknown type')
      }
    }
  }
}

function pushTokenSaga(action: PushGen.PushTokenPayload) {
  const {token, tokenType} = action.payload
  return Saga.sequentially([
    Saga.put(PushGen.createUpdatePushToken({token, tokenType})),
    Saga.put(PushGen.createSavePushToken()),
  ])
}

function* savePushTokenSaga(): Saga.SagaGenerator<any, any> {
  try {
    const state: TypedState = yield Saga.select()
    const {token, tokenType} = pushSelector(state)
    const deviceID = deviceIDSelector(state)
    if (!deviceID) {
      throw new Error('No device available for saving push token')
    }
    if (!token) {
      throw new Error('No push token available to save')
    }

    const args = [
      {key: 'push_token', value: token},
      {key: 'device_id', value: deviceID},
      {key: 'token_type', value: tokenType || ''},
    ]

    yield Saga.call(RPCTypes.apiserverPostRpcPromise, {
      args,
      endpoint: 'device/push_token',
    })
  } catch (err) {
    logger.warn('Error trying to save push token:', err)
  }
}

function* configurePushSaga(): Saga.SagaGenerator<any, any> {
  if (isMobile) {
    const chan = yield Saga.call(configurePush)

    while (true) {
      const action = yield Saga.take(chan)
      yield Saga.put(action)
    }
  }
}

function* deletePushTokenSaga(): Saga.SagaGenerator<any, any> {
  try {
    const state: TypedState = yield Saga.select()
    const {tokenType} = pushSelector(state)
    if (!tokenType) {
      // No push token to remove.
      logger.info('Not deleting push token -- none to remove')
      return
    }

    const deviceID = deviceIDSelector(state)
    if (!deviceID) {
      throw new Error('No device id available for saving push token')
    }

    const args = [{key: 'device_id', value: deviceID}, {key: 'token_type', value: tokenType}]

    yield Saga.call(RPCTypes.apiserverDeleteRpcPromise, {
      endpoint: 'device/push_token',
      args: args,
    })
  } catch (err) {
    logger.warn('Error trying to delete push token:', err)
  }
}

function* pushSaga(): Saga.SagaGenerator<any, any> {
  yield Saga.safeTakeLatest(PushGen.permissionsRequest, permissionsRequestSaga)
  yield Saga.safeTakeLatestPure(PushGen.permissionsNo, permissionsNoSaga)
  yield Saga.safeTakeLatestPure(PushGen.pushToken, pushTokenSaga)
  yield Saga.safeTakeLatest(PushGen.savePushToken, savePushTokenSaga)
  yield Saga.safeTakeLatest(PushGen.configurePush, configurePushSaga)
  yield Saga.safeTakeEvery(PushGen.notification, pushNotificationSaga)
}

export default pushSaga

export {deletePushTokenSaga}
<commit_msg>Select convo when acting on a chat notification (#10049)<commit_after>// @flow
import logger from '../logger'
import * as ChatGen from './chat-gen'
import * as PushGen from './push-gen'
import * as ChatTypes from '../constants/types/rpc-chat-gen'
import * as Saga from '../util/saga'
import * as RPCTypes from '../constants/types/rpc-gen'
import {isMobile} from '../constants/platform'
import {chatTab} from '../constants/tabs'
import {switchTo} from './route-tree'
import {createShowUserProfile} from './profile-gen'
import {
  requestPushPermissions,
  configurePush,
  displayNewMessageNotification,
  clearAllNotifications,
  setNoPushPermissions,
} from './platform-specific'

import type {TypedState} from '../constants/reducer'

const pushSelector = ({push: {token, tokenType}}: TypedState) => ({token, tokenType})
const deviceIDSelector = ({config: {deviceID}}: TypedState) => deviceID

function permissionsNoSaga() {
  return Saga.sequentially([
    Saga.call(setNoPushPermissions),
    Saga.put(PushGen.createPermissionsRequesting({requesting: false})),
    Saga.put(PushGen.createPermissionsPrompt({prompt: false})),
  ])
}

function* permissionsRequestSaga(): Saga.SagaGenerator<any, any> {
  try {
    yield Saga.put(PushGen.createPermissionsRequesting({requesting: true}))

    logger.info('Requesting permissions')
    const permissions = yield Saga.call(requestPushPermissions)
    logger.info('Permissions:', permissions)
    // TODO(gabriel): Set permissions we have in store, might want it at some point?
  } finally {
    yield Saga.put(PushGen.createPermissionsRequesting({requesting: false}))
    yield Saga.put(PushGen.createPermissionsPrompt({prompt: false}))
  }
}

function* pushNotificationSaga(notification: PushGen.NotificationPayload): Saga.SagaGenerator<any, any> {
  logger.info('Push notification:', notification)
  const payload = notification.payload.notification
  if (payload) {
    // Handle types that are not from user interaction
    if (payload.type === 'chat.newmessageSilent') {
      logger.info('Push notification: silent notification received')
      try {
        const unboxRes = yield Saga.call(ChatTypes.localUnboxMobilePushNotificationRpcPromise, {
          convID: payload.c || '',
          // $FlowIssue payload.t isn't ConversationMembersType
          membersType: typeof payload.t === 'string' ? parseInt(payload.t) : payload.t,
          payload: payload.m || '',
          pushIDs: typeof payload.p === 'string' ? JSON.parse(payload.p) : payload.p,
        })
        if (payload.x && payload.x > 0) {
          const num = payload.x
          const ageMS = Date.now() - num * 1000
          if (ageMS > 15000) {
            logger.info('Push notification: silent notification is stale:', ageMS)
            return
          }
        }
        if (unboxRes) {
          yield Saga.call(displayNewMessageNotification, unboxRes, payload.c, payload.b, payload.d)
        }
      } catch (err) {
        logger.info('failed to unbox silent notification', err)
      }
    } else if (payload.type === 'chat.readmessage') {
      logger.info('Push notification: read message notification received')
      const b = typeof payload.b === 'string' ? parseInt(payload.b) : payload.b
      if (b === 0) {
        clearAllNotifications()
      }
    }

    // Handle types from user interaction
    if (payload.userInteraction) {
      if (payload.type === 'chat.newmessage') {
        const {convID} = payload
        // Check for conversation ID so we know where to navigate to
        if (!convID) {
          logger.error('Push chat notification payload missing conversation ID')
          return
        }
        yield Saga.put(ChatGen.createSelectConversation({conversationIDKey: convID, fromUser: true}))
        yield Saga.put(switchTo([chatTab]))
      } else if (payload.type === 'follow') {
        const {username} = payload
        if (!username) {
          logger.error('Follow notification payload missing username', JSON.stringify(payload))
          return
        }
        logger.info('Push notification: follow received, follower= ', username)
        yield Saga.put(createShowUserProfile({username}))
      } else {
        logger.error('Push notification payload missing or unknown type')
      }
    }
  }
}

function pushTokenSaga(action: PushGen.PushTokenPayload) {
  const {token, tokenType} = action.payload
  return Saga.sequentially([
    Saga.put(PushGen.createUpdatePushToken({token, tokenType})),
    Saga.put(PushGen.createSavePushToken()),
  ])
}

function* savePushTokenSaga(): Saga.SagaGenerator<any, any> {
  try {
    const state: TypedState = yield Saga.select()
    const {token, tokenType} = pushSelector(state)
    const deviceID = deviceIDSelector(state)
    if (!deviceID) {
      throw new Error('No device available for saving push token')
    }
    if (!token) {
      throw new Error('No push token available to save')
    }

    const args = [
      {key: 'push_token', value: token},
      {key: 'device_id', value: deviceID},
      {key: 'token_type', value: tokenType || ''},
    ]

    yield Saga.call(RPCTypes.apiserverPostRpcPromise, {
      args,
      endpoint: 'device/push_token',
    })
  } catch (err) {
    logger.warn('Error trying to save push token:', err)
  }
}

function* configurePushSaga(): Saga.SagaGenerator<any, any> {
  if (isMobile) {
    const chan = yield Saga.call(configurePush)

    while (true) {
      const action = yield Saga.take(chan)
      yield Saga.put(action)
    }
  }
}

function* deletePushTokenSaga(): Saga.SagaGenerator<any, any> {
  try {
    const state: TypedState = yield Saga.select()
    const {tokenType} = pushSelector(state)
    if (!tokenType) {
      // No push token to remove.
      logger.info('Not deleting push token -- none to remove')
      return
    }

    const deviceID = deviceIDSelector(state)
    if (!deviceID) {
      throw new Error('No device id available for saving push token')
    }

    const args = [{key: 'device_id', value: deviceID}, {key: 'token_type', value: tokenType}]

    yield Saga.call(RPCTypes.apiserverDeleteRpcPromise, {
      endpoint: 'device/push_token',
      args: args,
    })
  } catch (err) {
    logger.warn('Error trying to delete push token:', err)
  }
}

function* pushSaga(): Saga.SagaGenerator<any, any> {
  yield Saga.safeTakeLatest(PushGen.permissionsRequest, permissionsRequestSaga)
  yield Saga.safeTakeLatestPure(PushGen.permissionsNo, permissionsNoSaga)
  yield Saga.safeTakeLatestPure(PushGen.pushToken, pushTokenSaga)
  yield Saga.safeTakeLatest(PushGen.savePushToken, savePushTokenSaga)
  yield Saga.safeTakeLatest(PushGen.configurePush, configurePushSaga)
  yield Saga.safeTakeEvery(PushGen.notification, pushNotificationSaga)
}

export default pushSaga

export {deletePushTokenSaga}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import {GridList, GridTile} from 'material-ui/GridList';
import IconButton from 'material-ui/IconButton';
import StarBorder from 'material-ui/svg-icons/toggle/star-border';

const styles = {
  root: {
    display: 'flex',
    flexWrap: 'wrap',
    justifyContent: 'space-around'
  },
  gridList: {
    width: '90%',
    height: 'auto',
    overflowY: 'auto',
    margin: 'auto'
  },
  paper: {
    margin: 'auto'
  },
};

class UserVoteList extends Component {

  render() {
    return (

        <GridList
          style={styles.gridList}
          cols={0.1}
        >
          {this.props.upNext.map((tile) => (
            <GridTile
              key={tile.songId}
              onTouchTap={this.props.voteFor.bind(this, tile.songId)}
              title={tile.songTitle}
              actionIcon={<IconButton><StarBorder color='white' /></IconButton>}
            >

            </GridTile>
          ))}
        </GridList>

    )
  };
};

export default UserVoteList;
<commit_msg>data structure change fix<commit_after>import React, {Component} from 'react';
import {GridList, GridTile} from 'material-ui/GridList';
import IconButton from 'material-ui/IconButton';
import StarBorder from 'material-ui/svg-icons/toggle/star-border';

const styles = {
  root: {
    display: 'flex',
    flexWrap: 'wrap',
    justifyContent: 'space-around'
  },
  gridList: {
    width: '90%',
    height: 'auto',
    overflowY: 'auto',
    margin: 'auto'
  },
  paper: {
    margin: 'auto'
  },
};

class UserVoteList extends Component {

  render() {
    return (

        <GridList
          style={styles.gridList}
          cols={0.1}
        >
          {this.props.upNext.map((tile) => (
            <GridTile
              key={tile.data.songId}
              onTouchTap={this.props.voteFor.bind(this, tile.data.songId)}
              title={tile.data.songTitle}
              actionIcon={<IconButton><StarBorder color='white' /></IconButton>}
            >

            </GridTile>
          ))}
        </GridList>

    )
  };
};

export default UserVoteList;
<|endoftext|>


<commit_before>var FF3p6OrLess = FBTest.compareFirefoxVersion("3.6.*") <= 0;

function runTest()
{
    FBTest.sysout("testErrors.START");
    FBTest.openNewTab(basePath + "console/testErrors.html", function(win)
    {
        FBTest.selectPanel("console");
        FBTest.enableScriptPanel(function(win) // causes reload
        {
            FBTest.enableConsolePanel(function(win) // causes reload
            {
                 FBTest.selectPanel("console");
                 fireTest(win, 0);
            });
        });
    });
}

function fireTest(win, ith)
{
    var syntaxErrorMsg = FF3p6OrLess ? "missing ; before statement" :
        "identifier starts immediately after numeric literal";

    var buttons = [
        "syntaxError",
        "shallowError",
        "deepError",
        "throw",
        "uncaughtException1891",
        "domError"
    ];

    var titles = [
        syntaxErrorMsg,
        "foops is not defined",
        "B3 is not defined",
        "uncaught exception: hi",
        "String contains an invalid character",
        "Not enough arguments"
    ];

    var sources = ["2BeOrNot2Be(40)",
        "",
        /\/\*foo\*\/\s*B3\(\)\;\s*/,
        "",
        "",
        /\s*document\.getElementById\(\)\;\s*/
    ];

    if (ith >= buttons.length)
    {
        FBTest.testDone("testErrors.done");
        return;
    }

    var panelDoc = FBTest.getPanelDocument();
    var lookForLogRow = new MutationRecognizer(panelDoc.defaultView, "div",
        {"class": "logRow-errorMessage"});

    lookForLogRow.onRecognize(function sawLogRow(elt)
    {
        FBTest.progress("matched logRow-errorMessage with "+ith, elt);
        checkConsoleLogMessage(buttons[ith], elt, titles[ith], sources[ith]);
        setTimeout(function bindArgs() { return fireTest(win, ith+1); });
    });

    FBTest.progress("waiting for "+lookForLogRow.getDescription());

    var button = win.document.getElementById(buttons[ith]);
    FBTest.progress("clicking "+button.getAttribute("id"));
    FBTest.click(button);
}

function checkConsoleLogMessage(button, log, expectedTitle, expectedSource)
{
    var title = FW.FBL.getElementByClass(log, "errorTitle");
    var source = FW.FBL.getElementByClass(log, "errorSource");

    FBTest.compare(expectedTitle, title.textContent, "The "+button+
        " error message must be correct.");
    if (expectedSource)
    {
        var isCorrect = FBTest.compare(expectedSource, source.textContent, "The "+button+
            " error source must be correct.");
        if (!isCorrect)
        {
            var min = expectedSource.length < source.textContent.length ?
                expectedSource.length : source.textContent.length;

            for (var i = 0; i < min; i++)
            {
                var expected = expectedSource.charAt(i);
                var was = source.textContent.charAt(i);
                if (expected != was)
                    FBTest.progress(" source differs at "+i+" |"+expected+"| vs |"+was+"|");
            }

            if (expectedSource.length < source.textContent.length)
            {
                FBTest.progress("The error source has "+source.textContent.length+
                    " characters, next charCodeAt is |"+source.textContent.charAt(min)+
                    "|("+source.textContent.charCodeAt(min)+")");
            }

            if (expectedSource.length > source.textContent.length)
            {
                FBTest.progress("The expected source has "+expectedSource.length+
                    " characters, next charCodeAt is |"+expectedSource.charAt(min)+
                    "|("+expectedSource.charCodeAt(min)+")");
            }
        }
        else
        {
            FBTest.progress("expectedSource is correct");
        }
    }
}
<commit_msg>Firefox now prepends the type of error to the error messages<commit_after>var FF3p6OrLess = FBTest.compareFirefoxVersion("3.6.*") <= 0;

function runTest()
{
    FBTest.sysout("testErrors.START");
    FBTest.openNewTab(basePath + "console/testErrors.html", function(win)
    {
        FBTest.selectPanel("console");
        FBTest.enableScriptPanel(function(win) // causes reload
        {
            FBTest.enableConsolePanel(function(win) // causes reload
            {
                 FBTest.selectPanel("console");
                 fireTest(win, 0);
            });
        });
    });
}

function fireTest(win, ith)
{
    var syntaxErrorMsg = FF3p6OrLess ? "missing ; before statement" :
        /identifier starts immediately after numeric literal/;

    var buttons = [
        "syntaxError",
        "shallowError",
        "deepError",
        "throw",
        "uncaughtException1891",
        "domError"
    ];

    var titles = [
        syntaxErrorMsg,
        /foops is not defined/,
        /B3 is not defined/,
        /uncaught exception: hi/,
        /String contains an invalid character/,
        /Not enough arguments/
    ];

    var sources = ["2BeOrNot2Be(40)",
        "",
        /\/\*foo\*\/\s*B3\(\)\;\s*/,
        "",
        "",
        /\s*document\.getElementById\(\)\;\s*/
    ];

    if (ith >= buttons.length)
    {
        FBTest.testDone("testErrors.done");
        return;
    }

    var panelDoc = FBTest.getPanelDocument();
    var lookForLogRow = new MutationRecognizer(panelDoc.defaultView, "div",
        {"class": "logRow-errorMessage"});

    lookForLogRow.onRecognize(function sawLogRow(elt)
    {
        FBTest.progress("matched logRow-errorMessage with "+ith, elt);
        checkConsoleLogMessage(buttons[ith], elt, titles[ith], sources[ith]);
        setTimeout(function bindArgs() { return fireTest(win, ith+1); });
    });

    FBTest.progress("waiting for "+lookForLogRow.getDescription());

    var button = win.document.getElementById(buttons[ith]);
    FBTest.progress("clicking "+button.getAttribute("id"));
    FBTest.click(button);
}

function checkConsoleLogMessage(button, log, expectedTitle, expectedSource)
{
    var title = FW.FBL.getElementByClass(log, "errorTitle");
    var source = FW.FBL.getElementByClass(log, "errorSource");

    FBTest.compare(expectedTitle, title.textContent, "The "+button+
        " error message must be correct.");
    if (expectedSource)
    {
        var isCorrect = FBTest.compare(expectedSource, source.textContent, "The "+button+
            " error source must be correct.");
        if (!isCorrect)
        {
            var min = expectedSource.length < source.textContent.length ?
                expectedSource.length : source.textContent.length;

            for (var i = 0; i < min; i++)
            {
                var expected = expectedSource.charAt(i);
                var was = source.textContent.charAt(i);
                if (expected != was)
                    FBTest.progress(" source differs at "+i+" |"+expected+"| vs |"+was+"|");
            }

            if (expectedSource.length < source.textContent.length)
            {
                FBTest.progress("The error source has "+source.textContent.length+
                    " characters, next charCodeAt is |"+source.textContent.charAt(min)+
                    "|("+source.textContent.charCodeAt(min)+")");
            }

            if (expectedSource.length > source.textContent.length)
            {
                FBTest.progress("The expected source has "+expectedSource.length+
                    " characters, next charCodeAt is |"+expectedSource.charAt(min)+
                    "|("+expectedSource.charCodeAt(min)+")");
            }
        }
        else
        {
            FBTest.progress("expectedSource is correct");
        }
    }
}
<|endoftext|>


<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';

import TouchRipple from '../TouchRipple';

import './Toast.css';

export default class Toast extends Component {

    constructor(props) {

        super(props);

        this.hasMounted = false;
        this.unrenderTimeout = null;

        this.state = {
            hidden: true,
            leave: false
        };

        this.getIconCls = this::this.getIconCls;
        this.clickHandle = this::this.clickHandle;
        this.mouseOverHandle = this::this.mouseOverHandle;
        this.mouseOutHandle = this::this.mouseOutHandle;

    }

    getIconCls() {
        switch (this.props.type) {
            case 'success':
                return 'fa fa-check-circle';
            case 'warning':
                return 'fa fa-exclamation-triangle';
            case 'error':
                return 'fa fa-times-circle';
            default:
                return 'fa fa-info-circle';
        }
    }

    clickHandle() {
        const {onRequestClose, toastsId} = this.props;
        onRequestClose && onRequestClose(toastsId);
    }

    initializeAnimation(callback) {
        setTimeout(() => {
            this.hasMounted && callback();
        }, 0);
    }

    animate() {
        this.setState({
            hidden: false
        });
    }

    mouseOverHandle() {
        if (this.unrenderTimeout) {
            clearTimeout(this.unrenderTimeout);
        }
    }

    mouseOutHandle() {

        const {onRequestClose, toastsId} = this.props;

        this.unrenderTimeout = setTimeout(() => {
            onRequestClose && onRequestClose(toastsId);
        }, 2500);

    }

    componentDidMount() {

        const {onRequestClose, toastsId} = this.props;

        this.hasMounted = true;
        this.refs.toast.style.height = this.refs.toast.clientHeight + 'px';

        this.unrenderTimeout = setTimeout(() => {
            onRequestClose && onRequestClose(toastsId);
        }, 2500);

    }

    componentWillAppear(callback) {
        this.initializeAnimation(callback);
    }

    componentWillEnter(callback) {
        this.initializeAnimation(callback);
    }

    componentDidAppear() {
        this.animate();
    }

    componentDidEnter() {
        this.animate();
    }

    componentWillLeave(callback) {
        this.setState({
            hidden: true,
            leave: true
        }, () => {
            this.timeout = setTimeout(() => {
                this.hasMounted && callback();
            }, 1250);
        });
    }

    componentWillUnmount() {
        this.timeout && clearTimeout(this.timeout);
    }

    render() {

        const {className, style, type, title, message} = this.props;
        const {hidden, leave} = this.state;

        return (
            <div ref="toast"
                 className={`toast ${type ? `toast-${type}` : ''} ${hidden ? 'hidden' : ''}
                    ${leave ? 'leave' : ''} ${className}`}
                 style={style}
                 onClick={this.clickHandle}>

                <i className={`${this.getIconCls()} toast-icon`}
                   aria-hidden="true">

{title}

{message}

                <TouchRipple/>

        );

    }
};

Toast.Type = {
    INFO: 'info',
    SUCCESS: 'success',
    WARNING: 'warning',
    ERROR: 'error'
};

Toast.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    toastsId: PropTypes.number,
    type: PropTypes.oneOf(Object.keys(Toast.Type).map(key => Toast.Type[key])),
    title: PropTypes.any,
    message: PropTypes.any,

    onRequestClose: PropTypes.func

};

Toast.defaultProps = {

    className: '',
    style: null,

    toastsId: 0,
    type: Toast.Type.INFO,
    title: '',
    message: ''

};<commit_msg>update Toaster<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';

import TouchRipple from '../TouchRipple';

import './Toast.css';

export default class Toast extends Component {

    constructor(props) {

        super(props);

        this.hasMounted = false;
        this.unrenderTimeout = null;

        this.state = {
            hidden: true,
            leave: false
        };

        this.getIconCls = this::this.getIconCls;
        this.clickHandle = this::this.clickHandle;
        // this.mouseOverHandle = this::this.mouseOverHandle;
        // this.mouseOutHandle = this::this.mouseOutHandle;

    }

    getIconCls() {
        switch (this.props.type) {
            case 'success':
                return 'fa fa-check-circle';
            case 'warning':
                return 'fa fa-exclamation-triangle';
            case 'error':
                return 'fa fa-times-circle';
            default:
                return 'fa fa-info-circle';
        }
    }

    clickHandle() {
        const {onRequestClose, toastsId} = this.props;
        onRequestClose && onRequestClose(toastsId);
    }

    initializeAnimation(callback) {
        setTimeout(() => {
            this.hasMounted && callback();
        }, 0);
    }

    animate() {
        this.setState({
            hidden: false
        });
    }

    // mouseOverHandle() {
    //     if (this.unrenderTimeout) {
    //         clearTimeout(this.unrenderTimeout);
    //     }
    // }

    // mouseOutHandle() {
    //
    //     const {onRequestClose, toastsId} = this.props;
    //
    //     this.unrenderTimeout = setTimeout(() => {
    //         onRequestClose && onRequestClose(toastsId);
    //     }, 2500);
    //
    // }

    componentDidMount() {

        const {onRequestClose, toastsId} = this.props;

        this.hasMounted = true;
        this.refs.toast.style.height = this.refs.toast.clientHeight + 'px';

        this.unrenderTimeout = setTimeout(() => {
            onRequestClose && onRequestClose(toastsId);
        }, 2500);

    }

    componentWillAppear(callback) {
        this.initializeAnimation(callback);
    }

    componentWillEnter(callback) {
        this.initializeAnimation(callback);
    }

    componentDidAppear() {
        this.animate();
    }

    componentDidEnter() {
        this.animate();
    }

    componentWillLeave(callback) {
        this.setState({
            hidden: true,
            leave: true
        }, () => {
            this.timeout = setTimeout(() => {
                this.hasMounted && callback();
            }, 1250);
        });
    }

    componentWillUnmount() {
        this.timeout && clearTimeout(this.timeout);
    }

    render() {

        const {className, style, type, title, message} = this.props;
        const {hidden, leave} = this.state;

        return (
            <div ref="toast"
                 className={`toast ${type ? `toast-${type}` : ''} ${hidden ? 'hidden' : ''}
                    ${leave ? 'leave' : ''} ${className}`}
                 style={style}
                 onClick={this.clickHandle}>

                <i className={`${this.getIconCls()} toast-icon`}
                   aria-hidden="true">

{title}

{message}

                <TouchRipple/>

        );

    }
};

Toast.Type = {
    INFO: 'info',
    SUCCESS: 'success',
    WARNING: 'warning',
    ERROR: 'error'
};

Toast.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,

    toastsId: PropTypes.number,
    type: PropTypes.oneOf(Object.keys(Toast.Type).map(key => Toast.Type[key])),
    title: PropTypes.any,
    message: PropTypes.any,

    onRequestClose: PropTypes.func

};

Toast.defaultProps = {

    className: '',
    style: null,

    toastsId: 0,
    type: Toast.Type.INFO,
    title: '',
    message: ''

};<|endoftext|>