<commit_before>'use strict';

function VidoeCSSElement(base_wall, video_source) {
    this.__init__(base_wall, video_source);
}

VidoeCSSElement.prototype = {
    __init__: function(base_wall, video_source) {
        // Base code from : https://codepen.io/asjas/pen/pWawPm
        var w = (base_wall.width * .7).toString() + 'px';
        var h = (base_wall.height * .7).toString() + 'px';
        var p = base_wall.get_position();
        var nn = base_wall.get_normal();
        var n = new THREE.Vector3(nn.x + p.x, nn.y + p.y, nn.z + p.z);

        var div = document.createElement('div');
        div.style.width = w;
        div.style.height = h;
        div.style.backgroundColor = '#ff009b';

        this.iframe = document.createElement('iframe');
        this.iframe.style.width = w;
        this.iframe.style.height = h;
        this.iframe.style.border = '0px';
        this.iframe.src = ['https://www.youtube.com/embed/', video_source, '?rel=0'].join( '' );
        div.appendChild(this.iframe);

        this.object = new THREE.CSS3DObject(div);
        this.object.position.set(p.x, p.y, p.z);
        this.object.lookAt(n);

        //base_wall.object3D.add(this.object);

        /////

        //this.iframe.contentWindow.addEventListener('onclick', function(event) {
        //});

        /////

        /*
        this.iframe.contentWindow.addEventListener('onclick', function(event) {

            l('SIMULATING MOUSE CLICK?');

            var mouse_event = new MouseEvent('click', {
                'bubbles': true,
                'cancelable': true,
                'screenX': 200,
                'screenY': 200
            });

            mouse_event.clientX = 200;
            mouse_event.clientY = 200;

            this.iframe.dispatchEvent(mouse_event);

        }).bind(this);
        */

        base_wall.world.css_scene.add(this.object);
    },

    set_position_and_normal: function(position, normal) {
        var p = position;
        var nn = normal;
        var n = new THREE.Vector3(nn.x + p.x, nn.y + p.y, nn.z + p.z);
        this.object.position.set(p.x, p.y, p.z);
        this.object.lookAt(n);
    },

    trigger_click_event: function(x, y) {

        l('TODO : Trigger click event!');

        var mouse_event = new MouseEvent('click', {
            'view': window,
            'bubbles': true,
            'cancelable': true,
            'screenX': 200,
            'screenY': 200
        });

        this.iframe.dispatchEvent(mouse_event);

        var event = JSON.parse(JSON.stringify(mouse_event));

        //this.iframe.contentWindow.postMessage(event, '*');

        //this.iframe

        //this.iframe.simulate('click', mouse_event);
        //this.iframe.trigger('click');
    }
};

function FloatingVideo(world, entity) {
    this.__init__(world, entity);
}

FloatingVideo.prototype = {

    __init__: function(world, entity) {
        if (!is_defined(entity)) {
            this.create_new_floating_video(world);
        } else {
            this.load_floating_video(world, entity);
        }

        this.add_base_wall_functionality();

        this.base_wall.world.root_attachables.push(this.base_wall);
        this.base_wall.refresh_position_and_look_at();
    },

    update_position_for_video: function() {
        this.video.set_position_and_normal(this.base_wall.get_position(), this.base_wall.get_normal());
    },

    trigger_click_event: function(x, y) {
        this.video.trigger_click_event(x, y);
    },

    /*___  __    ___
     |__  |  \ |  |     |  |  /\  |    |
     |___ |__/ |  |     |/\| /~~\ |___ |___ */
    show_settings_wall: function() {
        this.settings_wall.force_display_self_and_all_child_attachments_recursively();
    },

    set_video: function() {
        var video_code = this.video_code_input.get_text();
        this.video_entity.set_property(ENTITY_PROPERTY_NAME, video_code);

        this.settings_wall.force_hide_self_and_all_child_attachments_recursively();
    },

    /*        ___                 __   __   ___      ___         __                __           __        __          __
     | |\ | |  |  |  /\  |       /  ` |__) |__   /\   |  | |\ | / _`     /\  |\ | |  \    |    /  \  /\  |  \ | |\ | / _`
     | | \| |  |  | /~~\ |___    \__, |  \ |___ /~~\  |  | | \| \__>    /~~\ | \| |__/    |___ \__/ /~~\ |__/ | | \| \__> */
    add_base_wall_functionality: function() {
        // Create the settings wall.
        this.settings_wall = this.base_wall.add_floating_wall_attachment(400, 400, null, null, 10, false);
        this.settings_wall.add_close_button();
        this.settings_wall.set_auto_adjust_height(true);
        this.settings_wall.manual_visibility = true;

        var settings_row = this.settings_wall.add_row();
        settings_row.add_2D_element([0, ONE_THIRD], 'Video Code: ', TYPE_CONSTANT);
        this.video_code_input = settings_row.add_2D_element([ONE_THIRD, 1], '', TYPE_INPUT);

        settings_row = this.settings_wall.add_row();
        settings_row.add_2D_button([0, 1], 'Set Video', null, this.set_video.bind(this));

        this.settings_wall.force_hide_self_and_all_child_attachments_recursively();

        // Create the settings button.
        var row = this.base_wall.add_row(-1);
        var icon_width = 16 / this.base_wall.width;

        var settings_button = row.add_2D_element([0, icon_width], ICON_SETTINGS, TYPE_ICON);
        this.base_wall.world.interactive_objects.push(settings_button);
        settings_button.engable = false;
        settings_button.set_engage_function(this.show_settings_wall.bind(this));

        // Create the delete button.
        var close_button = row.add_2D_element([icon_width, icon_width * 2], ICON_CROSS, TYPE_ICON);
        this.base_wall.world.interactive_objects.push(close_button);
        close_button.engable = false;
        // TODO : close_button.set_engage_function()
    },

    create_new_floating_video: function(world) {
        var data = get_player_blink_spot(200);

        this.base_wall = new FloatingWall(400, 300, data[0], data[1], world, true);
        this.base_wall.set_to_saveable(world.entity);

        this.video_entity = new Entity();
        this.video_entity.set_property(ENTITY_DEFAULT_PROPERTY_TYPE, ENTITY_TYPE_VIDEO);
        this.video_entity.add_parent(this.base_wall.get_self_entity());
    },

    load_floating_video: function(world, entity) {
        this.video_entity = entity;

        // Load the base wall.
        this.base_wall = new FloatingWall(400, 600, null, null, world, true);
        this.base_wall.load_from_entity_data(this.video_entity.get_parent());
        this.base_wall.block_input_on_inner_70_percent = true;

        this.video = new VidoeCSSElement(this.base_wall, this.video_entity.get_value(ENTITY_PROPERTY_NAME));
        this.base_wall.post_position_update = this.update_position_for_video.bind(this);

        this.base_wall.trigger_click_event = this.trigger_click_event.bind(this);
    }
};
<commit_msg>video click events 12<commit_after>'use strict';

function VidoeCSSElement(base_wall, video_source) {
    this.__init__(base_wall, video_source);
}

VidoeCSSElement.prototype = {
    __init__: function(base_wall, video_source) {
        // Base code from : https://codepen.io/asjas/pen/pWawPm
        var w = (base_wall.width * .7).toString() + 'px';
        var h = (base_wall.height * .7).toString() + 'px';
        var p = base_wall.get_position();
        var nn = base_wall.get_normal();
        var n = new THREE.Vector3(nn.x + p.x, nn.y + p.y, nn.z + p.z);

        var div = document.createElement('div');
        div.style.width = w;
        div.style.height = h;
        div.style.backgroundColor = '#ff009b';

        this.iframe = document.createElement('iframe');
        this.iframe.style.width = w;
        this.iframe.style.height = h;
        this.iframe.style.border = '0px';
        this.iframe.src = ['https://www.youtube.com/embed/', video_source, '?rel=0'].join( '' );
        div.appendChild(this.iframe);

        this.object = new THREE.CSS3DObject(div);
        this.object.position.set(p.x, p.y, p.z);
        this.object.lookAt(n);

        //base_wall.object3D.add(this.object);

        /////

        //this.iframe.contentWindow.addEventListener('onclick', function(event) {
        //});

        /////

        /*
        this.iframe.contentWindow.addEventListener('onclick', function(event) {

            l('SIMULATING MOUSE CLICK?');

            var mouse_event = new MouseEvent('click', {
                'bubbles': true,
                'cancelable': true,
                'screenX': 200,
                'screenY': 200
            });

            mouse_event.clientX = 200;
            mouse_event.clientY = 200;

            this.iframe.dispatchEvent(mouse_event);

        }).bind(this);
        */

        base_wall.world.css_scene.add(this.object);
    },

    // Base code from : https://stackoverflow.com/questions/6157929/how-to-simulate-a-mouse-click-using-javascript
    simulatedClick: function(target, options) {

        var event = target.ownerDocument.createEvent('MouseEvents'),
            options = options || {},
            opts = { // These are the default values, set up for un-modified left clicks
                type: 'click',
                canBubble: true,
                cancelable: true,
                view: target.ownerDocument.defaultView,
                detail: 1,
                screenX: 0, //The coordinates within the entire page
                screenY: 0,
                clientX: 0, //The coordinates within the viewport
                clientY: 0,
                ctrlKey: false,
                altKey: false,
                shiftKey: false,
                metaKey: false, //I *think* 'meta' is 'Cmd/Apple' on Mac, and 'Windows key' on Win. Not sure, though!
                button: 0, //0 = left, 1 = middle, 2 = right
                relatedTarget: null,
            };

        //Merge the options with the defaults
        for (var key in options) {
            if (options.hasOwnProperty(key)) {
                opts[key] = options[key];
            }
        }

        //Pass in the options
        event.initMouseEvent(
            opts.type,
            opts.canBubble,
            opts.cancelable,
            opts.view,
            opts.detail,
            opts.screenX,
            opts.screenY,
            opts.clientX,
            opts.clientY,
            opts.ctrlKey,
            opts.altKey,
            opts.shiftKey,
            opts.metaKey,
            opts.button,
            opts.relatedTarget
        );

        //Fire the event
        target.dispatchEvent(event);
    },

    /////

    set_position_and_normal: function(position, normal) {
        var p = position;
        var nn = normal;
        var n = new THREE.Vector3(nn.x + p.x, nn.y + p.y, nn.z + p.z);
        this.object.position.set(p.x, p.y, p.z);
        this.object.lookAt(n);
    },

    trigger_click_event: function(x, y) {

        l('TODO : Trigger click event!');

        var mouse_event = new MouseEvent('click', {
            'view': window,
            'bubbles': true,
            'cancelable': true,
            'screenX': 200,
            'screenY': 200
        });

        this.iframe.dispatchEvent(mouse_event);
        this.simulatedClick(this.iframe);

        var event = JSON.parse(JSON.stringify(mouse_event));

        //this.iframe.contentWindow.postMessage(event, '*');

        //this.iframe

        //this.iframe.simulate('click', mouse_event);
        //this.iframe.trigger('click');
    }
};

function FloatingVideo(world, entity) {
    this.__init__(world, entity);
}

FloatingVideo.prototype = {

    __init__: function(world, entity) {
        if (!is_defined(entity)) {
            this.create_new_floating_video(world);
        } else {
            this.load_floating_video(world, entity);
        }

        this.add_base_wall_functionality();

        this.base_wall.world.root_attachables.push(this.base_wall);
        this.base_wall.refresh_position_and_look_at();
    },

    update_position_for_video: function() {
        this.video.set_position_and_normal(this.base_wall.get_position(), this.base_wall.get_normal());
    },

    trigger_click_event: function(x, y) {
        this.video.trigger_click_event(x, y);
    },

    /*___  __    ___
     |__  |  \ |  |     |  |  /\  |    |
     |___ |__/ |  |     |/\| /~~\ |___ |___ */
    show_settings_wall: function() {
        this.settings_wall.force_display_self_and_all_child_attachments_recursively();
    },

    set_video: function() {
        var video_code = this.video_code_input.get_text();
        this.video_entity.set_property(ENTITY_PROPERTY_NAME, video_code);

        this.settings_wall.force_hide_self_and_all_child_attachments_recursively();
    },

    /*        ___                 __   __   ___      ___         __                __           __        __          __
     | |\ | |  |  |  /\  |       /  ` |__) |__   /\   |  | |\ | / _`     /\  |\ | |  \    |    /  \  /\  |  \ | |\ | / _`
     | | \| |  |  | /~~\ |___    \__, |  \ |___ /~~\  |  | | \| \__>    /~~\ | \| |__/    |___ \__/ /~~\ |__/ | | \| \__> */
    add_base_wall_functionality: function() {
        // Create the settings wall.
        this.settings_wall = this.base_wall.add_floating_wall_attachment(400, 400, null, null, 10, false);
        this.settings_wall.add_close_button();
        this.settings_wall.set_auto_adjust_height(true);
        this.settings_wall.manual_visibility = true;

        var settings_row = this.settings_wall.add_row();
        settings_row.add_2D_element([0, ONE_THIRD], 'Video Code: ', TYPE_CONSTANT);
        this.video_code_input = settings_row.add_2D_element([ONE_THIRD, 1], '', TYPE_INPUT);

        settings_row = this.settings_wall.add_row();
        settings_row.add_2D_button([0, 1], 'Set Video', null, this.set_video.bind(this));

        this.settings_wall.force_hide_self_and_all_child_attachments_recursively();

        // Create the settings button.
        var row = this.base_wall.add_row(-1);
        var icon_width = 16 / this.base_wall.width;

        var settings_button = row.add_2D_element([0, icon_width], ICON_SETTINGS, TYPE_ICON);
        this.base_wall.world.interactive_objects.push(settings_button);
        settings_button.engable = false;
        settings_button.set_engage_function(this.show_settings_wall.bind(this));

        // Create the delete button.
        var close_button = row.add_2D_element([icon_width, icon_width * 2], ICON_CROSS, TYPE_ICON);
        this.base_wall.world.interactive_objects.push(close_button);
        close_button.engable = false;
        // TODO : close_button.set_engage_function()
    },

    create_new_floating_video: function(world) {
        var data = get_player_blink_spot(200);

        this.base_wall = new FloatingWall(400, 300, data[0], data[1], world, true);
        this.base_wall.set_to_saveable(world.entity);

        this.video_entity = new Entity();
        this.video_entity.set_property(ENTITY_DEFAULT_PROPERTY_TYPE, ENTITY_TYPE_VIDEO);
        this.video_entity.add_parent(this.base_wall.get_self_entity());
    },

    load_floating_video: function(world, entity) {
        this.video_entity = entity;

        // Load the base wall.
        this.base_wall = new FloatingWall(400, 600, null, null, world, true);
        this.base_wall.load_from_entity_data(this.video_entity.get_parent());
        this.base_wall.block_input_on_inner_70_percent = true;

        this.video = new VidoeCSSElement(this.base_wall, this.video_entity.get_value(ENTITY_PROPERTY_NAME));
        this.base_wall.post_position_update = this.update_position_for_video.bind(this);

        this.base_wall.trigger_click_event = this.trigger_click_event.bind(this);
    }
};
<|endoftext|>


<commit_before>/* eslint max-len: 0 */
import React from 'react';
import PropTypes from 'prop-types';

// Redux
import withRedux from 'next-redux-wrapper';
import { initStore } from 'store';
import { getDataset, getPartner, getTools, getTags, setTools, setTags, setCountView } from 'redactions/exploreDataset';
import { toggleModal, setModalOptions } from 'redactions/modal';

// Explore Detail Component
import ExploreDetailHeader from 'components/explore-detail/explore-detail-header';
import ExploreDetailInfo from 'components/explore-detail/explore-detail-info';
import ExploreDetailRelatedTools from 'components/explore-detail/explore-detail-related-tools';
import ExploreDetailActions from 'components/explore-detail/explore-detail-actions';
import ExploreDetailTags from 'components/explore-detail/explore-detail-tags';
import ExploreDetailWidgetEditor from 'components/explore-detail/explore-detail-widget-editor';

// Components
import Page from 'components/app/layout/Page';
import Layout from 'components/app/layout/Layout';
import Title from 'components/ui/Title';
import ReadMore from 'components/ui/ReadMore';

import Banner from 'components/app/common/Banner';
import SimilarDatasets from 'components/app/explore/similar-datasets/similar-datasets';

// Utils
import { PARTNERS_CONNECTIONS } from 'utils/partners/partnersConnections';
import { TOOLS_CONNECTIONS } from 'utils/apps/toolsConnections';

// Utils
import {
  getDatasetMetadata,
  getDatasetName
} from 'components/explore-detail/explore-detail-helpers';

import Error from '../_error';

class ExploreDetail extends Page {
  static propTypes = {
    url: PropTypes.object.isRequired,
    user: PropTypes.object,
    locale: PropTypes.string.isRequired
  };

  static async getInitialProps(context) {
    const props = await super.getInitialProps(context);
    const { store, res } = context;

    await store.dispatch(getDataset(props.url.query.id));

    // Check if the dataset exists and it is published
    const { exploreDataset } = store.getState();
    const dataset = exploreDataset.data;
    if (!dataset && res) res.statusCode = 404;
    if (dataset && !dataset.published && res) res.statusCode = 404;

    const { id, vocabulary } = dataset;

    // Set tags
    const tags = vocabulary && vocabulary.length > 0 && vocabulary[0].tags;
    if (tags) {
      store.dispatch(setTags(tags));
    }

    // Load connected partner
    const partnerConnection = PARTNERS_CONNECTIONS.find(pc => pc.datasetId === id);
    if (partnerConnection) {
      await store.dispatch(getPartner(partnerConnection.partnerId));
    }

    // Set tools and load connected tools
    const toolsConnections = TOOLS_CONNECTIONS.filter(appC => appC.datasetId === id).map(v => v.appSlug);
    if (toolsConnections.length > 0) {
      store.dispatch(setTools(toolsConnections));
      await store.dispatch(getTools());
    }

    return { ...props };
  }

  /**
   * Component Lifecycle
   * - componentDidMount
   * - componentWillReceiveProps
   * - componentWillUnmount
  */
  componentDidMount() {
    this.props.setCountView();
    this.props.getTags();
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.url.query.id !== nextProps.url.query.id) {
      window.scrollTo(0, 0);
      this.props.setCountView();
      this.props.getTags();
    }
  }

  render() {
    const {
      url, user, exploreDataset
    } = this.props;

    const dataset = exploreDataset.data;
    const datasetName = getDatasetName(dataset);
    const metadata = getDatasetMetadata(dataset);

    if (exploreDataset && exploreDataset.error === 'Not Found') return <Error status={404} />;
    if (dataset && !dataset.published) return <Error status={404} />;

    return (
      <Layout
        title={datasetName}
        description={metadata.description || ''}
        category="Dataset"
        url={url}
        user={user}
        pageHeader
      >

          {/* PAGE HEADER */}

                  <ExploreDetailHeader />

          {/* DATASET INFO && ACTIONS */}

                  {/* Function */}
                  <ReadMore
                    text={metadata.description}
                  />

                  <ExploreDetailActions />

          {/* WIDGET EDITOR */}
          <ExploreDetailWidgetEditor />

          {/* METADATA */}

                  <ExploreDetailInfo />

                  {/* TAGS SECTION */}

Tags

                  <ExploreDetailTags />

                  {/* SIMILAR DATASETS */}

                        <Title className="-extrabig -secondary -p-secondary">
                          Similar datasets
                        </Title>

                        <SimilarDatasets
                          datasetIds={[dataset.id]}
                          onTagSelected={this.handleTagSelected}
                        />

          {/* RELATED TOOLS */}
          {exploreDataset.tools.active.length > 0 &&

                          <Title className="-extrabig -secondary -p-secondary">
                            Related tools
                          </Title>

                          <ExploreDetailRelatedTools />

          }

                  <Banner className="partners -text-center">

Take the pulse of our planet.

                    Launch Planet Pulse
                  </Banner>

      </Layout>
    );
  }
}

const mapStateToProps = state => ({
  // Store
  locale: state.common.locale,
  user: state.user,
  exploreDataset: state.exploreDataset
});

const mapDispatchToProps = {
  getDataset,
  getPartner,
  getTools,
  setTools,
  getTags,
  setTags,
  setCountView,
  toggleModal,
  setModalOptions
};

export default withRedux(initStore, mapStateToProps, mapDispatchToProps)(ExploreDetail);
<commit_msg>Fixes<commit_after>/* eslint max-len: 0 */
import React from 'react';
import PropTypes from 'prop-types';

// Redux
import withRedux from 'next-redux-wrapper';
import { initStore } from 'store';
import {
  getDataset,
  getPartner,
  getTools,
  getTags,
  setTools,
  setTags,
  setCountView
} from 'redactions/exploreDataset';

// Explore Detail Component
import ExploreDetailHeader from 'components/explore-detail/explore-detail-header';
import ExploreDetailInfo from 'components/explore-detail/explore-detail-info';
import ExploreDetailRelatedTools from 'components/explore-detail/explore-detail-related-tools';
import ExploreDetailActions from 'components/explore-detail/explore-detail-actions';
import ExploreDetailTags from 'components/explore-detail/explore-detail-tags';
import ExploreDetailWidgetEditor from 'components/explore-detail/explore-detail-widget-editor';

// Components
import Page from 'components/app/layout/Page';
import Layout from 'components/app/layout/Layout';

import Title from 'components/ui/Title';
import ReadMore from 'components/ui/ReadMore';
import Banner from 'components/app/common/Banner';

import SimilarDatasets from 'components/app/explore/similar-datasets/similar-datasets';

// Utils
import { PARTNERS_CONNECTIONS } from 'utils/partners/partnersConnections';
import { TOOLS_CONNECTIONS } from 'utils/apps/toolsConnections';
import {
  getDatasetMetadata,
  getDatasetName
} from 'components/explore-detail/explore-detail-helpers';

import Error from '../_error';

class ExploreDetail extends Page {
  static propTypes = {
    url: PropTypes.object.isRequired,
    user: PropTypes.object,
    exploreDataset: PropTypes.object,
    locale: PropTypes.string.isRequired
  };

  static async getInitialProps(context) {
    const props = await super.getInitialProps(context);
    const { store, res } = context;

    await store.dispatch(getDataset(props.url.query.id));

    // Check if the dataset exists and it is published
    const { exploreDataset } = store.getState();
    const dataset = exploreDataset.data;
    if (!dataset && res) res.statusCode = 404;
    if (dataset && !dataset.published && res) res.statusCode = 404;

    const { id, vocabulary } = dataset;

    // Set tags
    const tags = vocabulary && vocabulary.length > 0 && vocabulary[0].tags;
    if (tags) {
      store.dispatch(setTags(tags));
    }

    // Load connected partner
    const partnerConnection = PARTNERS_CONNECTIONS.find(pc => pc.datasetId === id);
    if (partnerConnection) {
      await store.dispatch(getPartner(partnerConnection.partnerId));
    }

    // Set tools and load connected tools
    const toolsConnections = TOOLS_CONNECTIONS.filter(appC => appC.datasetId === id).map(v => v.appSlug);
    if (toolsConnections.length > 0) {
      store.dispatch(setTools(toolsConnections));
      await store.dispatch(getTools());
    }

    return { ...props };
  }

  /**
   * Component Lifecycle
   * - componentDidMount
   * - componentWillReceiveProps
   * - componentWillUnmount
  */
  componentDidMount() {
    this.props.setCountView();
    this.props.getTags();
  }

  componentWillReceiveProps(nextProps) {
    if (this.props.url.query.id !== nextProps.url.query.id) {
      window.scrollTo(0, 0);
      this.props.setCountView();
      this.props.getTags();
    }
  }

  render() {
    const {
      url, user, exploreDataset
    } = this.props;

    const dataset = exploreDataset.data;
    const datasetName = getDatasetName(dataset);
    const metadata = getDatasetMetadata(dataset);

    if (exploreDataset && exploreDataset.error === 'Not Found') return <Error status={404} />;
    if (dataset && !dataset.published) return <Error status={404} />;

    return (
      <Layout
        title={datasetName}
        description={metadata.description || ''}
        category="Dataset"
        url={url}
        user={user}
        pageHeader
      >

          {/* PAGE HEADER */}

                  <ExploreDetailHeader />

          {/* DATASET INFO && ACTIONS */}

                  {/* Function */}
                  <ReadMore
                    text={metadata.description}
                  />

                  <ExploreDetailActions />

          {/* WIDGET EDITOR */}
          <ExploreDetailWidgetEditor />

          {/* METADATA */}

                  <ExploreDetailInfo />

                  {/* TAGS SECTION */}

Tags

                  <ExploreDetailTags />

                  {/* SIMILAR DATASETS */}

                        <Title className="-extrabig -secondary -p-secondary">
                          Similar datasets
                        </Title>

                        <SimilarDatasets
                          datasetIds={[dataset.id]}
                          onTagSelected={this.handleTagSelected}
                        />

          {/* RELATED TOOLS */}
          {exploreDataset.tools.active.length > 0 &&

                          <Title className="-extrabig -secondary -p-secondary">
                            Related tools
                          </Title>

                          <ExploreDetailRelatedTools />

          }

                  <Banner className="partners -text-center">

Take the pulse of our planet.

                    Launch Planet Pulse
                  </Banner>

      </Layout>
    );
  }
}

const mapStateToProps = state => ({
  // Store
  locale: state.common.locale,
  user: state.user,
  exploreDataset: state.exploreDataset
});

const mapDispatchToProps = {
  getDataset,
  getPartner,
  getTools,
  setTools,
  getTags,
  setTags,
  setCountView
};

export default withRedux(initStore, mapStateToProps, mapDispatchToProps)(ExploreDetail);
<|endoftext|>


<commit_before>F.ext({
	/**
	 * Animates the context according to the animation rules
	 * @param {Array|String} a Animations
	 * @param {Function} c Callback
	 * @param [i] placeholder
	 * @returns {*}
	 * @see https://github.com/misantronic/framewreck#animate-module
	 * @example F('#id').animate(['X:100 Y:50', 'O:0.5', 'X:0 Y:0', 'O:1'], function() {
	 * 	console.log("all done");
	 * });
	 */
	animate: function(a, c, i) {
		var _ = this;

		// animation shorthands
		a = a == 'hide' ? ['O:0'] : a == 'show' ? ['O:1'] : a;

		_.A  = a;
		_.Ac = c;

		for(i = _.x[F.L]; i--;)
			_._a(F(_.x[i]), 0);

		return _
	},

	_a: function(e, i) {
		var _		= this,
			anims	= _.A[i].split(" "),
			trs 	= [], // transition
			trf		= [], // transform
			obj		= {},
			map 	= {
				W: "width",
				H: "height",
				O: "opacity"
			};

		for(var k=0; k < anims[F.L]; k++) {
			var anim 	= anims[k],
				type 	= anim[0],						// animation type
				prop 	= anim.substr(2).split(","),	// property
				val 	= prop[0],						// value
				dur 	= prop[1] || 0.5, 				// duration
				del 	= parseFloat(prop[2]) || 0,		// delay
				cur		= e.css(map[type]);  			// current property value

			// reset width and height
			if(type == 'W')
				e.css({width: cur});
			if(type == 'H')
				e.css({height: cur});

			// set object property + value
			map[type] ? obj[map[type]] = val : map[type] = 'transform';

			// set transition
			trs[k] = map[type] + ' '+dur +'s linear '+ del +'s';

			// set transform
			trf.push(
				'translateX('+ (type == 'X' ? val : 0) +'px)',
				'translateY('+ (type == 'Y' ? val : 0) +'px)',
				'rotate('+ (type == 'R' ? val : 0) +'deg)',
				'scale('+ (type == 'S' ? val : 1) +')'
			);
		}

		if(trs.length)
			obj.transition = trs.join(",");

		if(trf.length)
			obj.transform = trf.join(" ");

		function h() {
			e.off('transitionend', h);

			_.A[++i]?_._a(e, i):_.Ac&&_.Ac.call(_)
		}

		// trigger animation on next tick
		setTimeout(function() {
			e.on("transitionend", h).css(obj)
		}, 0);

	}
});
//F.vendor = (Array.prototype.slice.call(getComputedStyle(F.d.documentElement, '')).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1];<commit_msg>shortened push<commit_after>F.ext({
	/**
	 * Animates the context according to the animation rules
	 * @param {Array|String} a Animations
	 * @param {Function} c Callback
	 * @param [i] placeholder
	 * @returns {*}
	 * @see https://github.com/misantronic/framewreck#animate-module
	 * @example F('#id').animate(['X:100 Y:50', 'O:0.5', 'X:0 Y:0', 'O:1'], function() {
	 * 	console.log("all done");
	 * });
	 */
	animate: function(a, c, i) {
		var _ = this;

		// animation shorthands
		a = a == 'hide' ? ['O:0'] : a == 'show' ? ['O:1'] : a;

		_.A  = a;
		_.Ac = c;

		for(i = _.x[F.L]; i--;)
			_._a(F(_.x[i]), 0);

		return _
	},

	_a: function(e, i) {
		var _		= this,
			anims	= _.A[i].split(" "),
			trs 	= [], // transition
			trf		= [], // transform
			obj		= {},
			map 	= {
				W: "width",
				H: "height",
				O: "opacity"
			};

		for(var k=0; k < anims[F.L]; k++) {
			var anim 	= anims[k],
				type 	= anim[0],						// animation type
				prop 	= anim.substr(2).split(","),	// property
				val 	= prop[0],						// value
				dur 	= prop[1] || 0.5, 				// duration
				del 	= parseFloat(prop[2]) || 0,		// delay
				cur		= e.css(map[type]);  			// current property value

			// reset width and height
			if(type == 'W')
				e.css({width: cur});
			if(type == 'H')
				e.css({height: cur});

			// set object property + value
			map[type] ? obj[map[type]] = val : map[type] = 'transform';

			// set transition
			trs[k] = map[type] + ' '+dur +'s linear '+ del +'s';

			// set transform
			trf[k] = 'translateX('+ (type == 'X' ? val : 0) +'px) translateY('+ (type == 'Y' ? val : 0) +'px) rotate('+ (type == 'R' ? val : 0) +'deg) scale('+ (type == 'S' ? val : 1) +')';
		}

		if(trs.length)
			obj.transition = trs.join(",");

		if(trf.length)
			obj.transform = trf.join(" ");

		function h() {
			e.off('transitionend', h);

			_.A[++i]?_._a(e, i):_.Ac&&_.Ac.call(_)
		}

		// trigger animation on next tick
		setTimeout(function() {
			e.on("transitionend", h).css(obj)
		}, 0);

	}
});
//F.vendor = (Array.prototype.slice.call(getComputedStyle(F.d.documentElement, '')).join('').match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o']))[1];<|endoftext|>


<commit_before>'use strict';

/**
 * Requirements
 */
const EntityTemplateRoute = require(ES_SOURCE + '/server/route/EntityTemplateRoute.js').EntityTemplateRoute;
const CliLogger = require(ES_SOURCE + '/cli/CliLogger.js').CliLogger;
const Environment = require(ES_SOURCE + '/nunjucks/Environment.js').Environment;
const Filters = require(ES_SOURCE + '/nunjucks/filter/index.js');
const Plugins = require(ES_SOURCE + '/model/viewmodel/plugin/index.js');
const routeSpec = require('./RouteShared.js').spec;
const projectFixture = require(ES_FIXTURES + '/project/index.js');
const request = require('supertest');

/**
 * Spec
 */
describe(EntityTemplateRoute.className, function()
{
    /**
     * Route Test
     */
    routeSpec(EntityTemplateRoute, 'server.route/EntityTemplateRoute', function(parameters)
    {
        const cliLogger = new CliLogger('', { muted: true });
        return [cliLogger, global.fixtures.urlsConfiguration, global.fixtures.nunjucks];
    });

    /**
     * EntityTemplateRoute Test
     */
    beforeEach(function()
    {
        global.fixtures = projectFixture.createDynamic();
        global.fixtures.cliLogger = new CliLogger('', { muted: true });
        global.fixtures.viewModelRepository.plugins.push(new Plugins.ViewModelLipsumPlugin());
        global.fixtures.viewModelRepository.plugins.push(new Plugins.ViewModelImagePlugin());
        global.fixtures.viewModelRepository.plugins.push(new Plugins.ViewModelImportPlugin());
        global.fixtures.nunjucks = new Environment(global.fixtures.entitiesRepository,
            global.fixtures.buildConfiguration,
            [
                new Filters.LoadFilter(global.fixtures.viewModelRepository),
                new Filters.ImageUrlFilter(['image']),
                new Filters.ModuleClassesFilter(),
                new Filters.EmptyFilter(),
                new Filters.NotEmptyFilter(),
                new Filters.MediaQueryFilter(global.fixtures.globalConfiguration)
            ],
            { basePath: global.fixtures.pathesConfiguration.sites });
    });

    // Create a initialized testee
    const createTestee = function()
    {
        const cliLogger = new CliLogger('', { muted: true });
        return new EntityTemplateRoute(cliLogger,
            global.fixtures.urlsConfiguration,
            global.fixtures.nunjucks,
            { basePath: global.fixtures.pathesConfiguration.sites });
    };

    describe('serving...', function()
    {
        it('should serve .j2 files from a entity directory', function(done)
        {
            const testee = createTestee();
            routeSpec.createServer([testee]);
            global.fixtures.server.addRoute(testee);
            global.fixtures.server.start().then(function(server)
            {
                request(server)
                      .get('/base/pages/p-start/p-start.j2')
                      .expect(200)
                      .expect(/m-teaser/m)
                      .expect(/e-headline/m)
                      .expect(/g-teaserlist/m, done);
            });
        });

        it('should only serve .j2 files', function(done)
        {
            const testee = createTestee();
            routeSpec.createServer([testee]);
            global.fixtures.server.addRoute(testee);
            global.fixtures.server.start().then(function(server)
            {
                request(server)
                      .get('/base/pages/p-start/p-start.md')
                      .expect(404, done);
            });
        });

        it('should only serve existing files', function(done)
        {
            const testee = createTestee();
            routeSpec.createServer([testee]);
            global.fixtures.server.addRoute(testee);
            global.fixtures.server.start().then(function(server)
            {
                request(server)
                      .get('/base/pages/p-start/p-start-msiing.j2')
                      .expect(404, done);
            });
        });
    });
});
<commit_msg>test(server.route.EntityTemplateRoute): adds pathesConfiguration dependency<commit_after>'use strict';

/**
 * Requirements
 */
const EntityTemplateRoute = require(ES_SOURCE + '/server/route/EntityTemplateRoute.js').EntityTemplateRoute;
const CliLogger = require(ES_SOURCE + '/cli/CliLogger.js').CliLogger;
const Environment = require(ES_SOURCE + '/nunjucks/Environment.js').Environment;
const Filters = require(ES_SOURCE + '/nunjucks/filter/index.js');
const Plugins = require(ES_SOURCE + '/model/viewmodel/plugin/index.js');
const routeSpec = require('./RouteShared.js').spec;
const projectFixture = require(ES_FIXTURES + '/project/index.js');
const request = require('supertest');

/**
 * Spec
 */
describe(EntityTemplateRoute.className, function()
{
    /**
     * Route Test
     */
    routeSpec(EntityTemplateRoute, 'server.route/EntityTemplateRoute', function(parameters)
    {
        const cliLogger = new CliLogger('', { muted: true });
        return [cliLogger, global.fixtures.urlsConfiguration, global.fixtures.nunjucks];
    });

    /**
     * EntityTemplateRoute Test
     */
    beforeEach(function()
    {
        global.fixtures = projectFixture.createDynamic();
        global.fixtures.cliLogger = new CliLogger('', { muted: true });
        global.fixtures.viewModelRepository.plugins.push(new Plugins.ViewModelLipsumPlugin());
        global.fixtures.viewModelRepository.plugins.push(new Plugins.ViewModelImagePlugin());
        global.fixtures.viewModelRepository.plugins.push(new Plugins.ViewModelImportPlugin());
        global.fixtures.nunjucks = new Environment(global.fixtures.entitiesRepository,
            global.fixtures.pathesConfiguration,
            global.fixtures.buildConfiguration,
            [
                new Filters.LoadFilter(global.fixtures.viewModelRepository),
                new Filters.ImageUrlFilter(['image']),
                new Filters.ModuleClassesFilter(),
                new Filters.EmptyFilter(),
                new Filters.NotEmptyFilter(),
                new Filters.MediaQueryFilter(global.fixtures.globalConfiguration)
            ],
            { basePath: global.fixtures.pathesConfiguration.sites });
    });

    // Create a initialized testee
    const createTestee = function()
    {
        const cliLogger = new CliLogger('', { muted: true });
        return new EntityTemplateRoute(cliLogger,
            global.fixtures.urlsConfiguration,
            global.fixtures.nunjucks,
            { basePath: global.fixtures.pathesConfiguration.sites });
    };

    describe('serving...', function()
    {
        it('should serve .j2 files from a entity directory', function(done)
        {
            const testee = createTestee();
            routeSpec.createServer([testee]);
            global.fixtures.server.addRoute(testee);
            global.fixtures.server.start().then(function(server)
            {
                request(server)
                    .get('/base/pages/p-start/p-start.j2')
                    .expect(200)
                    .expect(/m-teaser/m)
                    .expect(/e-headline/m)
                    .expect(/g-teaserlist/m, done);
            });
        });

        it('should only serve .j2 files', function(done)
        {
            const testee = createTestee();
            routeSpec.createServer([testee]);
            global.fixtures.server.addRoute(testee);
            global.fixtures.server.start().then(function(server)
            {
                request(server)
                    .get('/base/pages/p-start/p-start.md')
                    .expect(404, done);
            });
        });

        it('should only serve existing files', function(done)
        {
            const testee = createTestee();
            routeSpec.createServer([testee]);
            global.fixtures.server.addRoute(testee);
            global.fixtures.server.start().then(function(server)
            {
                request(server)
                    .get('/base/pages/p-start/p-start-msiing.j2')
                    .expect(404, done);
            });
        });
    });
});
<|endoftext|>


<commit_before>import React from 'react';
import { shallow } from 'enzyme';

import SwitchBox from 'control_new/components/forms/SwitchBox';

describe('<SwitchBox>', () => {
  const props = {
    children: null,
    onChange: () => {},
    value: null,
  };

  it('should work', () => {
    const wrapper = () => shallow(<SwitchBox {...props} />);

    expect(wrapper).not.toThrow();
  });
});
<commit_msg>SwitchBox tests<commit_after>import { Switch } from 'antd';
import React from 'react';
import { mount } from 'enzyme';

import SwitchBox from 'control_new/components/forms/SwitchBox';

describe('<SwitchBox>', () => {
  const props = {
    children: null,
    onChange: () => {},
    value: null,
  };

  const factory = (customProps = {}) => mount(<SwitchBox {...props} {...customProps} />);

  it('should work', () => {
    expect(factory).not.toThrow();
  });

  describe('the label', () => {
    it('should not render if there is no label', () => {
      const wrapper = factory();
      expect(wrapper.find('.label').length).toBe(0);
    });

    it('should render the passed in children', () => {
      const label = 'Root beer\'s flavor comes from sassafras and/or sarsaparilla!';
      const wrapper = factory({ children: label });

      // The label should render..
      expect(wrapper.find('.label').length).toBe(1);
      // ..And it should contain the text we passed in.
      expect(wrapper.find('.label').text()).toContain(label);
    });

    it('should trigger an onChange when clicked', () => {
      let called = false;
      const handleChange = () => { called = true; };
      const wrapper = factory({ onChange: handleChange, children: 'Click me!' });

      wrapper.find('.label').simulate('click');
      expect(called).toBe(true);
    });
  });

  describe('the Switch', () => {
    it('should inherit the `value` as a `checked` prop', () => {
      const wrapper = factory({ value: true });
      expect(wrapper.find(Switch).length).toBe(1);
      expect(wrapper.find(Switch).props().checked).toBe(true);

      wrapper.setProps({ value: false });
      expect(wrapper.find(Switch).props().checked).toBe(false);
    });
  });
});
<|endoftext|>


<commit_before>import GitHubAPI from 'github'
import config from 'config'
import Build from 'server/models/Build'
import crashReporter from 'modules/crashReporter/crashReporter'
import { formatUrlFromBuild } from 'modules/urls/buildUrl'

export class NotifyError extends Error {}

const notifyStatus = async (buildId, { state, description }) => {
  const build = await Build.query().findById(buildId)
    .eager('[repository, repository.user, repository.organization, compareScreenshotBucket]')

  if (!build) {
    throw new NotifyError('Build not found')
  }

  const [user] = await build.getUsers()

  if (!user) {
    throw new NotifyError('No user found')
  }

  const owner = build.repository.user || build.repository.organization

  if (!owner) {
    throw new NotifyError('Owner not found')
  }

  const github = new GitHubAPI({
    debug: config.get('env') === 'development',
  })

  github.authenticate({
    type: 'oauth',
    token: user.accessToken,
  })

  const buildUrl = await formatUrlFromBuild(build, { absolute: true })

  // https://developer.github.com/v3/repos/statuses/
  return github.repos.createStatus({
    owner: owner.name,
    repo: build.repository.name,
    sha: build.compareScreenshotBucket.commit,
    state,
    description, // Short description of the status.
    target_url: buildUrl,
    context: 'argos',
  })
}

const softNotifyStatus = async (buildId, status) => {
  try {
    return notifyStatus(buildId, status)
  } catch (error) {
    crashReporter.captureException(error)
    return null
  }
}

export const notifyProgress = buildId => softNotifyStatus(buildId, {
  state: 'pending',
  description: 'Argos build in progress',
})

export const notifySuccess = buildId => softNotifyStatus(buildId, {
  state: 'success',
  description: 'Argos build success',
})

export const notifyFailure = buildId => softNotifyStatus(buildId, {
  state: 'failure',
  description: 'Argos build failure',
})

export default notifyStatus
<commit_msg>feat: remove error status<commit_after>import GitHubAPI from 'github'
import config from 'config'
import Build from 'server/models/Build'
import crashReporter from 'modules/crashReporter/crashReporter'
import { formatUrlFromBuild } from 'modules/urls/buildUrl'

export class NotifyError extends Error {}

const notifyStatus = async (buildId, { state, description }) => {
  const build = await Build.query().findById(buildId)
    .eager('[repository, repository.user, repository.organization, compareScreenshotBucket]')

  if (!build) {
    throw new NotifyError('Build not found')
  }

  const [user] = await build.getUsers()

  if (!user) {
    throw new NotifyError('No user found')
  }

  const owner = build.repository.user || build.repository.organization

  if (!owner) {
    throw new NotifyError('Owner not found')
  }

  const github = new GitHubAPI({
    debug: config.get('env') === 'development',
  })

  github.authenticate({
    type: 'oauth',
    token: user.accessToken,
  })

  const buildUrl = await formatUrlFromBuild(build, { absolute: true })

  // https://developer.github.com/v3/repos/statuses/
  return github.repos.createStatus({
    owner: owner.name,
    repo: build.repository.name,
    sha: build.compareScreenshotBucket.commit,
    state,
    description, // Short description of the status.
    target_url: buildUrl,
    context: 'argos',
  })
}

const softNotifyStatus = async (buildId, status) => {
  try {
    return notifyStatus(buildId, status)
  } catch (error) {
    crashReporter.captureException(error)
    return null
  }
}

export const notifyProgress = buildId => softNotifyStatus(buildId, {
  state: 'pending',
  description: 'Argos build is in progress.',
})

export const notifySuccess = buildId => softNotifyStatus(buildId, {
  state: 'success',
  description: 'All screenshots are good!',
})

export const notifyFailure = buildId => softNotifyStatus(buildId, {
  state: 'success',
  description: 'A difference in a screenshot has been detected.',
})

export default notifyStatus
<|endoftext|>


<commit_before>
/**
 *
 * @param {number} start
 * @param {number} end
 * @param {?number} step
 * @return {Range<number>}
 */
function range(start, end, step = null) {
  if (!Number.isSafeInteger(start)) {
    throw new TypeError(
      `The starting value must be a safe integer, ${start} was provided`
    )
  }
  if ((typeof end !== 'number') || (Math.floor(end) !== end)) {
    throw new TypeError(
      `The ending value must be an integer or infinity, ${end} was provided`
    )
  }
  if (
    Number.isFinite(end) &&
    ((end < Number.MIN_SAFE_INTEGER) || (end > Number.MAX_SAFE_INTEGER))
  ) {
    throw new RangeError(
      `The ending value must be within the [Number.MIN_SAFE_INTEGER, ` +
      `Number.MAX_SAFE_INTEGER] range or infinite, ${end} was provided`
    )
  }

  if (step === null) {
    step = end >= start ? 1 : -1
  } else {
    if (!Number.isSafeInteger(step)) {
      throw new TypeError(
        `The step must be either a null or a safe integer, ${step} was ` +
        `provided`
      )
    }
    if (!step) {
      throw new RangeError(
        `The step cannot be 0`
      )
    }
  }

  /**
   * @template E
   */
  class Range {
    /**
     * @template P
     * @param {?function(E, number, Range): boolean} filter
     * @param {?function(P, number, Range): *} map
     * @param {?function(E, number, Range): boolean} stop
     * @param {?Range<P>} parentRange
     */
    constructor(filter, map, stop, parentRange) {
      /**
       * @type {?function(E, number, Range): boolean}
       */
      this._filter = filter

      /**
       * @type {?function(*, number, Range): E}
       */
      this._map = map

      /**
       * @type {E}
       */
      this._currentValue = start

      /**
       * @type {number}
       */
      this._index = 0

      /**
       * @type {?function(E, number, Range): boolean}
       */
      this._stop = stop

      /**
       * @type {Range}
       */
      this._parentRange = parentRange

      /**
       * @type {{next: function(): {done: boolean, value: *}}}
       */
      this._iterator = parentRange || createIterator(this)

      /**
       * @type {?number}
       */
      this._length = null

      /**
       * @type {[{done: boolean, value: E}, number][]}
       */
      this._preGeneratedValues = []

      /**
       * @type {[{done: boolean, value: E}, number][]}
       */
      this._originalValues = null

      /**
       * @type {number}
       */
      this._generatedValuesCount = 0

      /**
       * @type {boolean}
       */
      this._isFinite = Number.isFinite(end)
    }

    /**
     * @return {number}
     */
    get length() {
      if (this._length !== null) {
        return this._length
      }

      if (!this._isFinite) {
        let currentRange = this
        let isFilteredRange = !!this._filter
        while (!isFilteredRange && currentRange._parentRange) {
          currentRange = currentRange._parentRange
          isFilteredRange = !!currentRange._filter
        }
        if (isFilteredRange) {
          console.warn(
            'The length of filtered infinite ranges is always assumed to be ' +
            'Infinity, even if that is not the case, because there is no ' +
            'way to reliably determine if an infinitely-generated filtered ' +
            'range would end up having a finite number of elements in a ' +
            'finite time, given the fact that the filter is a callback.'
          )
        }

        this._length = Number.POSITIVE_INFINITY
      } else if (!this._filter && !this._stop) {
        if (this._parentRange) {
          this._length = this._parentRange.length
        } else {
          this._length = Math.floor((end - start) / step) +
              (((end - start) % step) ? 1 : 0)
        }
      } else {
        let nextIteration = getNextValue(this, this._index)
        while (!nextIteration[0].done) {
          this._preGeneratedValues.push(nextIteration)
          nextIteration = getNextValue(this, nextIteration[1])
        }
        this._length =
            this._generatedValuesCount + this._preGeneratedValues.length
      }

      return this._length
    }

    /**
     * @return {{done: boolean, value: E}}
     */
    next() {
      let iteration, nextIndex
      if (this._preGeneratedValues.length) {
        [iteration, nextIndex] = this._preGeneratedValues.shift()
      } else {
        [iteration, nextIndex] = getNextValue(this, this._index)
      }

      this._index = nextIndex
      this._currentValue = iteration.value
      this._generatedValuesCount++

      return iteration
    }

    /**
     * @return {Range<[number, E]>}
     */
    enumerate() {
      return this.map((value, index) => [index, value])
    }

    /**
     * @return {Range<E>}
     * @throws {Error} Thrown if this range is infinite.
     */
    reverse() {
      if (!this._isFinite) {
        throw new Error('Infinite ranges cannot be reversed')
      }

      if (!this._parentRange) {
        let newEnd = this._currentValue
        if (!this._index) {
          newEnd -= step
        }
        return range(end - step, newEnd, -step)
      }

      let values = [...this.clone()]
      let reversedRange = new Range(null, null, null, range(0, 0))
      reversedRange._currentValue = end
      reversedRange._preGeneratedValues = values.reverse().map(
        (value, index) => [{ done: false, value }, index + 1]
      )
      reversedRange._originalValues = reversedRange._preGeneratedValues.slice()
      reversedRange._length = values.length
      return reversedRange
    }

    /**
     * @template R
     * @param {function(E, number, Range): R} mapCallback
     * @return {Range<R>}
     */
    map(mapCallback) {
      return new Range(null, mapCallback, null, this)
    }

    /**
     * Returns a new sequence that filters the values produced by this sequence
     * using the provided callback.
     *
     * @param {function(E, number, Range): boolean} filterCallback The callback
     *        that will be used to filter the values of this sequence. The
     *        first argument will be the value, the second will be the index of
     *        the value in the returned sequence should it pass the filter, and
     *        the last will be the returned sequence.
     * @return {Range<E>} A sequence that produces the values of this sequence
     *         filtered through thr provided callback.
     */
    filter(filterCallback) {
      return new Range(filterCallback, null, null, this)
    }

    /**
     * Returns a sequence that produces up to the specified number of values
     * from this sequence.
     *
     * The returned sequence will be marked as finite, even if this sequence is
     * infinite.
     *
     * @param {number} count The maximum number of elements the returned
     *        sequence should be able to produce. Must be a safe non-negative
     *        integer.
     * @return {Range<E>} A sequence that will produce only up to the specified
     *         number of elements before terminating.
     */
    take(count) {
      if (!Number.isSafeInteger(count) || (count < 0)) {
        throw new TypeError(
          `The count limit must be a non-negative safe integer, ${count} ` +
          `was provided`
        )
      }

      let limitedRange = this.takeWhile((_, index) => index <= count)
      limitedRange._isFinite = true
      return limitedRange
    }

    /**
     * Returns a sequence that produces values from this sequence while the
     * provided precondition is satisfied.
     *
     * @param {function(E, number, Range): boolean} precondition The
     *        precondition that will be used to test a value to determine
     *        whether to include it in the returned sequence. The first
     *        argument will be the candidate value, the second argument will be
     *        its index in the sequence, and the third will be the returned
     *        sequence itself.
     * @return {Range<E>} A sequence that produces the values from this
     *         sequence while the provided precondition holds true.
     */
    takeWhile(precondition) {
      return new Range(null, null, precondition, this)
    }

    /**
     * Reduces the elements of this sequence using the provided operation into
     * a single value. This method works in the same way as the
     * {@linkcode Array.prototype.reduce} method.
     *
     * The method cannot be used on infinite sequences and will throw an error.
     *
     * @template I
     * @template R
     * @param {I} initialValue The initial value for the first argument of the
     *        provided operation function.
     * @param {function((I|R), E, number, Range): R} operation The operation to
     *        apply on the elements of this sequence in order to produce the
     *        resulting value. The first argument will either be the provided
     *        initial value or a mid-result value, the second argument will be
     *        the currently processed element of the sequence, the third will
     *        be the elements index, and the last argument is this sequence
     *        itself.
     * @return {R} The resulting computed value.
     * @throws {Error} Thrown if called on an infinite sequence.
     */
    reduce(initialValue, operation) {
      if (!this._isFinite) {
        throw new Error(
          'The reduce() method cannot be applied to infinite sequences'
        )
      }

      let result = initialValue
      for (let value of this) {
        result = operation(result, value, this._index, this)
      }
      return result
    }

    /**
     * Resets this sequence to its start, allowing it to generate the same
     * values again.
     */
    reset() {
      if (this._parentRange) {
        this._parentRange.reset()
      } else {
        this._currentValue = start
        this._iterator = createIterator(this)
      }
      this._index = 0
      this._generatedValuesCount = 0;
      if (this._originalValues) {
        this._preGeneratedValues = this._originalValues.slice();
      } else {
        this._preGeneratedValues = [];
      }
    }

    /**
     * Creates a clone of of this sequence. The clone will generate the same
     * values as this sequence from now on. If this sequence has already been
     * used to generate values, these values will not appear in the clone.
     *
     * @return {Range<E>} A clone of this sequence.
     */
    clone() {
      let parentClone = this._parentRange ? this._parentRange.clone() : null
      let clone = new Range(this._filter, this._map, this._stop, parentClone)
      clone._currentValue = this._currentValue
      clone._index = this._index
      if (!this._parentRange && this._index) {
        clone._iterator.next() // shift the iterator after the current value
      }
      clone._length = this._length
      clone._preGeneratedValues = this._preGeneratedValues.slice()
      clone._generatedValuesCount = this._generatedValuesCount
      return clone
    }

    /**
     * Exports the remaining values in this sequence into an array (this
     * sequence will be iterated into its end).
     *
     * Infinite sequences cannot be turned into an array and cause this method
     * to throw an error. This method is therefore a safer way of turning
     * sequences into arrays as compared to {@code [...sequence]}, because
     * using the spread operator (...) on an infinite sequence would enter the
     * code into and infinite operation and effectively freeze the application.
     *
     * @return {E[]} The generated array of values remaining in this sequence.
     * @throws {Error} Thrown if this sequence is infinite.
     */
    toArray() {
      if (!this._isFinite) {
        throw new Error(
          'The infinite sequences cannot be exported to an array'
        )
      }

      return [...this]
    }

    /**
     * Returns the iterator of this {@linkcode Range} instance, which is the
     * instance itself.
     *
     * This method is used to enable compatibility with the for-of cycles.
     *
     * @return {Range<E>} This instance.
     */
    [Symbol.iterator]() {
      return this
    }
  }

  /**
   * Generates the next iteration (the value and a flag whether the end of the
   * sequence has been reached) and the index of the iteration in the sequence
   * of values represented by the provided {@linkcode Range} instance.
   *
   * @template E
   * @param {Range<E>} range The {@linkcode Range} instance representing the
   *        sequence of values for which the next value should be generated.
   * @param {number} currentIndex The index of the previously generated value
   *        in the sequence.
   * @return {[{done: boolean, value: E}, number]} A tuple representing the
   *         next iteration step and the index of the value in the sequence.
   */
  function getNextValue(range, currentIndex) {
    let iteration = range._iterator.next()
    let nextIndex
    if (currentIndex < Number.MAX_SAFE_INTEGER) {
      nextIndex = currentIndex + 1
    } else {
      nextIndex = Number.POSITIVE_INFINITY
    }

    if (iteration.done) {
      return [iteration, nextIndex]
    }

    if (range._filter && !range._filter(iteration.value, nextIndex, range)) {
      return getNextValue(range, currentIndex)
    }

    if (range._map) {
      iteration.value = range._map(iteration.value, nextIndex, range)
    }

    if (range._stop && !range._stop(iteration.value, nextIndex, range)) {
      return [{done: true, value: undefined}, nextIndex]
    }

    return [iteration, nextIndex]
  }

  /**
   * Creates an iterator for generating a sequence of numeric values
   * represented by the provided {@linkcode Range} instance.
   *
   * @param {Range<number>} rangeInstance The {@linkcode Range} instance for
   *        generating a sequence of numbers for which the iterator should be
   *        created.
   * @return {{next: function(): {done: boolean, value: number}}} The created
   *         iterator that will generate the numeric values for the provided
   *         {@linkcode Range} instance.
   */
  function createIterator(rangeInstance) {
    return (function * () {
      while (!isEnded()) {
        yield rangeInstance._currentValue

        let currentSafeLimit = Number.MAX_SAFE_INTEGER - Math.abs(step)
        if (currentSafeLimit < Math.abs(rangeInstance._currentValue)) {
          rangeInstance._currentValue =
              Math.sign(rangeInstance._currentValue) * Infinity
        } else {
          rangeInstance._currentValue += step
        }
      }
    })()

    function isEnded() {
      if (!Number.isFinite(end)) {
        return false
      }

      return (step > 0) ?
          (rangeInstance._currentValue >= end) :
          (rangeInstance._currentValue <= end)
    }
  }

  return new Range(null, null, null, null)
}

if ((typeof module !== 'undefined') && (typeof exports !== 'undefined')) {
  exports.default = range
  exports.range = range
}
<commit_msg>documentation update<commit_after>
/**
 *
 * @param {number} start
 * @param {number} end
 * @param {?number} step
 * @return {Range<number>}
 */
function range(start, end, step = null) {
  if (!Number.isSafeInteger(start)) {
    throw new TypeError(
      `The starting value must be a safe integer, ${start} was provided`
    )
  }
  if ((typeof end !== 'number') || (Math.floor(end) !== end)) {
    throw new TypeError(
      `The ending value must be an integer or infinity, ${end} was provided`
    )
  }
  if (
    Number.isFinite(end) &&
    ((end < Number.MIN_SAFE_INTEGER) || (end > Number.MAX_SAFE_INTEGER))
  ) {
    throw new RangeError(
      `The ending value must be within the [Number.MIN_SAFE_INTEGER, ` +
      `Number.MAX_SAFE_INTEGER] range or infinite, ${end} was provided`
    )
  }

  if (step === null) {
    step = end >= start ? 1 : -1
  } else {
    if (!Number.isSafeInteger(step)) {
      throw new TypeError(
        `The step must be either a null or a safe integer, ${step} was ` +
        `provided`
      )
    }
    if (!step) {
      throw new RangeError(
        `The step cannot be 0`
      )
    }
  }

  /**
   * @template E
   */
  class Range {
    /**
     * @template P
     * @param {?function(E, number, Range): boolean} filter
     * @param {?function(P, number, Range): *} map
     * @param {?function(E, number, Range): boolean} stop
     * @param {?Range<P>} parentRange
     */
    constructor(filter, map, stop, parentRange) {
      /**
       * @type {?function(E, number, Range): boolean}
       */
      this._filter = filter

      /**
       * @type {?function(*, number, Range): E}
       */
      this._map = map

      /**
       * @type {E}
       */
      this._currentValue = start

      /**
       * @type {number}
       */
      this._index = 0

      /**
       * @type {?function(E, number, Range): boolean}
       */
      this._stop = stop

      /**
       * @type {Range}
       */
      this._parentRange = parentRange

      /**
       * @type {{next: function(): {done: boolean, value: *}}}
       */
      this._iterator = parentRange || createIterator(this)

      /**
       * @type {?number}
       */
      this._length = null

      /**
       * @type {[{done: boolean, value: E}, number][]}
       */
      this._preGeneratedValues = []

      /**
       * @type {[{done: boolean, value: E}, number][]}
       */
      this._originalValues = null

      /**
       * @type {number}
       */
      this._generatedValuesCount = 0

      /**
       * @type {boolean}
       */
      this._isFinite = Number.isFinite(end)
    }

    /**
     * Returns the length of this sequence (the number of values the sequence
     * is able to produce in total).
     *
     * Note that the filtered infinite sequences cannot have their length
     * determined and this property will return {@code Infinite} for them.
     *
     * @return {number} The number of values this sequence is able to produce
     *         in total.
     */
    get length() {
      if (this._length !== null) {
        return this._length
      }

      if (!this._isFinite) {
        let currentRange = this
        let isFilteredRange = !!this._filter
        while (!isFilteredRange && currentRange._parentRange) {
          currentRange = currentRange._parentRange
          isFilteredRange = !!currentRange._filter
        }
        if (isFilteredRange) {
          console.warn(
            'The length of filtered infinite ranges is always assumed to be ' +
            'Infinity, even if that is not the case, because there is no ' +
            'way to reliably determine if an infinitely-generated filtered ' +
            'range would end up having a finite number of elements in a ' +
            'finite time, given the fact that the filter is a callback.'
          )
        }

        this._length = Number.POSITIVE_INFINITY
      } else if (!this._filter && !this._stop) {
        if (this._parentRange) {
          this._length = this._parentRange.length
        } else {
          this._length = Math.floor((end - start) / step) +
              (((end - start) % step) ? 1 : 0)
        }
      } else {
        let nextIteration = getNextValue(this, this._index)
        while (!nextIteration[0].done) {
          this._preGeneratedValues.push(nextIteration)
          nextIteration = getNextValue(this, nextIteration[1])
        }
        this._length =
            this._generatedValuesCount + this._preGeneratedValues.length
      }

      return this._length
    }

    /**
     * Returns the next iteration step of this sequence.
     *
     * @return {{done: boolean, value: E}} The next iteration step.
     * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
     */
    next() {
      let iteration, nextIndex
      if (this._preGeneratedValues.length) {
        [iteration, nextIndex] = this._preGeneratedValues.shift()
      } else {
        [iteration, nextIndex] = getNextValue(this, this._index)
      }

      this._index = nextIndex
      this._currentValue = iteration.value
      this._generatedValuesCount++

      return iteration
    }

    /**
     * Returns a new sequence that will generate a two-tuples (arrays)
     * containing the remaining values from this sequence with their indexes in
     * the returned sequence.
     *
     * The index will be the first element of the returned tuple, the original
     * value will be the second element of the returned tuple.
     *
     * @return {Range<[number, E]>} A sequence generating the remaining values
     *         and their indexes in tuples.
     */
    enumerate() {
      return this.map((value, index) => [index, value])
    }

    /**
     * Returns a new sequence that will produce the remaining values in this
     * sequence in the reverse order.
     *
     * Infinite sequences cannot be reversed.
     *
     * @return {Range<E>} A sequence that will produce the reamining values in
     *         this sequence in the reverse order.
     * @throws {Error} Thrown if this range is infinite.
     */
    reverse() {
      if (!this._isFinite) {
        throw new Error('Infinite ranges cannot be reversed')
      }

      if (!this._parentRange) {
        let newEnd = this._currentValue
        if (!this._index) {
          newEnd -= step
        }
        return range(end - step, newEnd, -step)
      }

      let values = [...this.clone()]
      let reversedRange = new Range(null, null, null, range(0, 0))
      reversedRange._currentValue = end
      reversedRange._preGeneratedValues = values.reverse().map(
        (value, index) => [{ done: false, value }, index + 1]
      )
      reversedRange._originalValues = reversedRange._preGeneratedValues.slice()
      reversedRange._length = values.length
      return reversedRange
    }

    /**
     * Returns a new sequence that maps the values produced by this sequence
     * using the provided callback to new values.
     *
     * @template R
     * @param {function(E, number, Range): R} mapCallback The callback that
     *        will be used to transform the values of this sequence. This first
     *        argument will be the value, the second will be the index of the
     *        value in the returned sequence, and the last will be the returned
     *        sequence.
     * @return {Range<R>} A sequence that produces the values of this sequence
     *         transformed using the provided callback.
     */
    map(mapCallback) {
      return new Range(null, mapCallback, null, this)
    }

    /**
     * Returns a new sequence that filters the values produced by this sequence
     * using the provided callback.
     *
     * @param {function(E, number, Range): boolean} filterCallback The callback
     *        that will be used to filter the values of this sequence. The
     *        first argument will be the value, the second will be the index of
     *        the value in the returned sequence should it pass the filter, and
     *        the last will be the returned sequence.
     * @return {Range<E>} A sequence that produces the values of this sequence
     *         filtered through thr provided callback.
     */
    filter(filterCallback) {
      return new Range(filterCallback, null, null, this)
    }

    /**
     * Returns a sequence that produces up to the specified number of values
     * from this sequence.
     *
     * The returned sequence will be marked as finite, even if this sequence is
     * infinite.
     *
     * @param {number} count The maximum number of elements the returned
     *        sequence should be able to produce. Must be a safe non-negative
     *        integer.
     * @return {Range<E>} A sequence that will produce only up to the specified
     *         number of elements before terminating.
     */
    take(count) {
      if (!Number.isSafeInteger(count) || (count < 0)) {
        throw new TypeError(
          `The count limit must be a non-negative safe integer, ${count} ` +
          `was provided`
        )
      }

      let limitedRange = this.takeWhile((_, index) => index <= count)
      limitedRange._isFinite = true
      return limitedRange
    }

    /**
     * Returns a sequence that produces values from this sequence while the
     * provided precondition is satisfied.
     *
     * @param {function(E, number, Range): boolean} precondition The
     *        precondition that will be used to test a value to determine
     *        whether to include it in the returned sequence. The first
     *        argument will be the candidate value, the second argument will be
     *        its index in the sequence, and the third will be the returned
     *        sequence itself.
     * @return {Range<E>} A sequence that produces the values from this
     *         sequence while the provided precondition holds true.
     */
    takeWhile(precondition) {
      return new Range(null, null, precondition, this)
    }

    /**
     * Reduces the elements of this sequence using the provided operation into
     * a single value. This method works in the same way as the
     * {@linkcode Array.prototype.reduce} method.
     *
     * The method cannot be used on infinite sequences and will throw an error.
     *
     * @template I
     * @template R
     * @param {I} initialValue The initial value for the first argument of the
     *        provided operation function.
     * @param {function((I|R), E, number, Range): R} operation The operation to
     *        apply on the elements of this sequence in order to produce the
     *        resulting value. The first argument will either be the provided
     *        initial value or a mid-result value, the second argument will be
     *        the currently processed element of the sequence, the third will
     *        be the elements index, and the last argument is this sequence
     *        itself.
     * @return {R} The resulting computed value.
     * @throws {Error} Thrown if called on an infinite sequence.
     */
    reduce(initialValue, operation) {
      if (!this._isFinite) {
        throw new Error(
          'The reduce() method cannot be applied to infinite sequences'
        )
      }

      let result = initialValue
      for (let value of this) {
        result = operation(result, value, this._index, this)
      }
      return result
    }

    /**
     * Resets this sequence to its start, allowing it to generate the same
     * values again.
     */
    reset() {
      if (this._parentRange) {
        this._parentRange.reset()
      } else {
        this._currentValue = start
        this._iterator = createIterator(this)
      }
      this._index = 0
      this._generatedValuesCount = 0;
      if (this._originalValues) {
        this._preGeneratedValues = this._originalValues.slice();
      } else {
        this._preGeneratedValues = [];
      }
    }

    /**
     * Creates a clone of of this sequence. The clone will generate the same
     * values as this sequence from now on. If this sequence has already been
     * used to generate values, these values will not appear in the clone.
     *
     * @return {Range<E>} A clone of this sequence.
     */
    clone() {
      let parentClone = this._parentRange ? this._parentRange.clone() : null
      let clone = new Range(this._filter, this._map, this._stop, parentClone)
      clone._currentValue = this._currentValue
      clone._index = this._index
      if (!this._parentRange && this._index) {
        clone._iterator.next() // shift the iterator after the current value
      }
      clone._length = this._length
      clone._preGeneratedValues = this._preGeneratedValues.slice()
      clone._generatedValuesCount = this._generatedValuesCount
      return clone
    }

    /**
     * Exports the remaining values in this sequence into an array (this
     * sequence will be iterated into its end).
     *
     * Infinite sequences cannot be turned into an array and cause this method
     * to throw an error. This method is therefore a safer way of turning
     * sequences into arrays as compared to {@code [...sequence]}, because
     * using the spread operator (...) on an infinite sequence would enter the
     * code into and infinite operation and effectively freeze the application.
     *
     * @return {E[]} The generated array of values remaining in this sequence.
     * @throws {Error} Thrown if this sequence is infinite.
     */
    toArray() {
      if (!this._isFinite) {
        throw new Error(
          'The infinite sequences cannot be exported to an array'
        )
      }

      return [...this]
    }

    /**
     * Returns the iterator of this {@linkcode Range} instance, which is the
     * instance itself.
     *
     * This method is used to enable compatibility with the for-of cycles.
     *
     * @return {Range<E>} This instance.
     */
    [Symbol.iterator]() {
      return this
    }
  }

  /**
   * Generates the next iteration (the value and a flag whether the end of the
   * sequence has been reached) and the index of the iteration in the sequence
   * of values represented by the provided {@linkcode Range} instance.
   *
   * @template E
   * @param {Range<E>} range The {@linkcode Range} instance representing the
   *        sequence of values for which the next value should be generated.
   * @param {number} currentIndex The index of the previously generated value
   *        in the sequence.
   * @return {[{done: boolean, value: E}, number]} A tuple representing the
   *         next iteration step and the index of the value in the sequence.
   */
  function getNextValue(range, currentIndex) {
    let iteration = range._iterator.next()
    let nextIndex
    if (currentIndex < Number.MAX_SAFE_INTEGER) {
      nextIndex = currentIndex + 1
    } else {
      nextIndex = Number.POSITIVE_INFINITY
    }

    if (iteration.done) {
      return [iteration, nextIndex]
    }

    if (range._filter && !range._filter(iteration.value, nextIndex, range)) {
      return getNextValue(range, currentIndex)
    }

    if (range._map) {
      iteration.value = range._map(iteration.value, nextIndex, range)
    }

    if (range._stop && !range._stop(iteration.value, nextIndex, range)) {
      return [{done: true, value: undefined}, nextIndex]
    }

    return [iteration, nextIndex]
  }

  /**
   * Creates an iterator for generating a sequence of numeric values
   * represented by the provided {@linkcode Range} instance.
   *
   * @param {Range<number>} rangeInstance The {@linkcode Range} instance for
   *        generating a sequence of numbers for which the iterator should be
   *        created.
   * @return {{next: function(): {done: boolean, value: number}}} The created
   *         iterator that will generate the numeric values for the provided
   *         {@linkcode Range} instance.
   */
  function createIterator(rangeInstance) {
    return (function * () {
      while (!isEnded()) {
        yield rangeInstance._currentValue

        let currentSafeLimit = Number.MAX_SAFE_INTEGER - Math.abs(step)
        if (currentSafeLimit < Math.abs(rangeInstance._currentValue)) {
          rangeInstance._currentValue =
              Math.sign(rangeInstance._currentValue) * Infinity
        } else {
          rangeInstance._currentValue += step
        }
      }
    })()

    function isEnded() {
      if (!Number.isFinite(end)) {
        return false
      }

      return (step > 0) ?
          (rangeInstance._currentValue >= end) :
          (rangeInstance._currentValue <= end)
    }
  }

  return new Range(null, null, null, null)
}

if ((typeof module !== 'undefined') && (typeof exports !== 'undefined')) {
  exports.default = range
  exports.range = range
}
<|endoftext|>


<commit_before>export default function without(arr, ...args) {
  if (!Array.isArray(arr)) {
    arr = Array.from(arr);
  }

  return arr.reduce((result, val) => {
    if (!args.includes(val)) {
      result.push(val);
    }

    return result;
  }, []);
}
<commit_msg>Simpler _without.<commit_after>export default function without(arr, ...args) {
  if (!Array.isArray(arr)) {
    arr = Array.from(arr);
  }

  return arr.filter((val) => !args.includes(val));
}
<|endoftext|>