<commit_before>/* eslint no-unused-expressions: 0 */
/* global sinon */

import React from "react";
import { defaults, reduce } from "lodash";
import { mount } from "enzyme";
import { Data, addEvents } from "src/index";

describe("victory-util/add-events", () => {
  class MockDataComponent extends React.Component {
    static displayName = "MockDataComponent";
    static role = "dataComponent";

    render() {
      const { datum, events, style } = this.props;
      return (

          `${datum.x}: ${datum.y}`

      );
    }
  }

  class MockLabel extends React.Component {
    static displayName = "MockLabel";
    static role = "label";

    render() {
      const { events, text } = this.props;

      return (

          `${text}`

      );
    }
  }

  class MockChart extends React.Component {
    static displayName = "MockChart";
    static role = "chart";

    static defaultProps = {
      dataComponent: MockDataComponent,
      labelComponent: React.createElement(MockLabel, { text: 'label' }),
      groupComponent: "div"
    };

    static getBaseProps = (props) => {
      const data = Data.getData(props.data);
      const childProps = reduce(data, (accum, datum, index) => {
        return defaults({}, accum, {
          [index]: {
            data: {
              index,
              datum,
              data
            }
          }
        });
      }, {});

      return {
        parent: { data },
        ...childProps
      };
    };

    render() {
      const props = defaults({}, this.props, this.defaultProps);
      const { dataComponent, labelComponent, groupComponent } = props;
      const dataComponents = [];
      const labelComponents = [];
      for (let index = 0, len = this.dataKeys.length; index < len; index++) {
        const dataProps = this.getComponentProps(dataComponent, "data", index);
        dataComponents[index] = React.createElement(dataComponent, dataProps);

        const labelProps = this.getComponentProps(labelComponent, "labels", index);
        if (labelProps && labelProps.text !== undefined && labelProps.text !== null) {
          labelComponents[index] = React.cloneElement(labelComponent, labelProps);
        }
      }

      return React.createElement(groupComponent, {}, ...dataComponents, ...labelComponents);
    }
  }

  it.only("do stuff", () => {
    const EventedMockChart = addEvents(MockChart);
    const spy = sinon.spy();

    const wrapper = mount(
      <EventedMockChart
        data={[{ x: 1, y: 2 }, { x: 3, y: 4 }]}
        events={[
          {
            target: "data",
            eventHandlers: {
              onClick: () => {
                return [{
                  target: "data",
                  mutation: (props) => {
                    spy();
                  }
                }];
              }
            }
          }
        ]}
      />
    );

    const firstDataComponent = wrapper.find(MockDataComponent).filterWhere((node) => {
      return node.props().datum.x === 1;
    });

    expect(spy).not.to.have.been.called;
    firstDataComponent.simulate('click');
    expect(spy).to.have.been.called;
  });
});
<commit_msg>remove unnecessary label details<commit_after>/* eslint no-unused-expressions: 0 */
/* global sinon */

import React from "react";
import { defaults, reduce } from "lodash";
import { mount } from "enzyme";
import { Data, addEvents } from "src/index";

describe("victory-util/add-events", () => {
  class MockDataComponent extends React.Component {
    static displayName = "MockDataComponent";
    static role = "dataComponent";

    render() {
      const { datum, events, style } = this.props;
      return (

          `${datum.x}: ${datum.y}`

      );
    }
  }

  class MockLabel extends React.Component {
    static displayName = "MockLabel";
    static role = "label";

    render() {
      const { text } = this.props;

      return (

`${text}`

      );
    }
  }

  class MockChart extends React.Component {
    static displayName = "MockChart";
    static role = "chart";

    static defaultProps = {
      dataComponent: MockDataComponent,
      labelComponent: React.createElement(MockLabel, { text: 'label' }),
      groupComponent: "div"
    };

    static getBaseProps = (props) => {
      const data = Data.getData(props.data);
      const childProps = reduce(data, (accum, datum, index) => {
        return defaults({}, accum, {
          [index]: {
            data: {
              index,
              datum,
              data
            }
          }
        });
      }, {});

      return {
        parent: { data },
        ...childProps
      };
    };

    render() {
      const props = defaults({}, this.props, this.defaultProps);
      const { dataComponent, labelComponent, groupComponent } = props;
      const dataComponents = [];
      const labelComponents = [];
      for (let index = 0, len = this.dataKeys.length; index < len; index++) {
        const dataProps = this.getComponentProps(dataComponent, "data", index);
        dataComponents[index] = React.createElement(dataComponent, dataProps);

        const labelProps = this.getComponentProps(labelComponent, "labels", index);
        if (labelProps && labelProps.text !== undefined && labelProps.text !== null) {
          labelComponents[index] = React.cloneElement(labelComponent, labelProps);
        }
      }

      return React.createElement(groupComponent, {}, ...dataComponents, ...labelComponents);
    }
  }

  it.only("do stuff", () => {
    const EventedMockChart = addEvents(MockChart);
    const spy = sinon.spy();

    const wrapper = mount(
      <EventedMockChart
        data={[{ x: 1, y: 2 }, { x: 3, y: 4 }]}
        events={[
          {
            target: "data",
            eventHandlers: {
              onClick: () => {
                return [{
                  target: "data",
                  mutation: (props) => {
                    spy();
                  }
                }];
              }
            }
          }
        ]}
      />
    );

    const firstDataComponent = wrapper.find(MockDataComponent).filterWhere((node) => {
      return node.props().datum.x === 1;
    });

    expect(spy).not.to.have.been.called;
    firstDataComponent.simulate('click');
    expect(spy).to.have.been.called;
  });
});
<|endoftext|>


<commit_before>/* jshint undef: true, strict:false, trailing:false, unused:false, -W110 */
/* global require, exports, console, process, module, describe, it, expect, jasmine, angular, _, $ */
angular.module('timon',['indx', 'ngAnimate'])
	.controller('main', function($scope, client, utils, transformers) {
		var store = client.store,
			u = utils, box, diffQs = [],
			sa = function(fn) { return u.safeApply($scope, fn); },
			guid = u.guid(),
			get_user = function(store) {
				var d = u.deferred();
				store.checkLogin().then(function(l) {
					if (l && l.user_metadata) {
						var um = JSON.parse(l.user_metadata);
						if (um && um.name) { return d.resolve(l, um.name); }
					}
					d.resolve(l, l.username);
				}).fail(function(err) { console.error(err); d.reject(); });
				return d.promise();
			};

		$scope._ = _;
		$scope.input = {};

		var initialise = function(boxid) {
			// kill previous queries
			if (!(boxid && boxid.length > 0)) { return ; }

			$scope.followers = [];
			$scope.following = {};
			$scope.channels = {};
			$scope.timeline = {};
			$scope.people = [];
			$scope.test = {};

			diffQs.map(function(q) { q.stop(); });
			diffQs = [];

			store.getBox(boxid).then(function(b) {
				window.b = b;
				box = b;
				b.standingQuery({ type:'micropost' }, function(message) {
					sa(function() { $scope.timeline[message.id] = message; });
				}).then(function(diffid) { diffQs.push(diffid); }).fail(function(err) {
					console.error('error setting up standing query for following ', err);
				});
				b.standingQuery({ type:'microfollow' }, function(following) {
					sa(function() { $scope.following[following.peek('url')] = following; });
				}).then(function(diffid) { diffQs.push(diffid); }).fail(function(err) {
					console.error('error setting up standing query for following ', err);
				});
				b.standingQuery({ type:'channel' }, function(cobj) {
					sa(function() { $scope.channels[cobj.id] = cobj; });
				}).then(function(diffid) { diffQs.push(diffid); }).fail(function(err) {
					console.error('error setting up standing query for following ', err);
				});

				get_user(store).then(function(login, name) {
					createChannel(b,login,name);
					$scope.login = login;
					$scope.name = name;
				});

			}).fail(function(err) {  console.error('error getting box ', err); });

		};
		var getChannelURL = function(store,box,id) {
			var url = [store._getBaseURL(), box.id, id].join('/');
			console.info('channel url ', url);
			return url;
		};
		var createChannel = function(box, ownerlogin, ownername) {
			var d = u.deferred();
			box.query({type:'channel'}).then(function(x) {
				if (x.length == 0) {
					var id = 'channel-main';
					console.log(' no channels ');
					box.obj([id, ownerlogin.username]).then(function(objs) {
						var ch = objs[0], user = objs[1];
						ch.set({type:'channel', owner:user, ownername:ownername, created: new Date(), url:getChannelURL(store,box,id)});
						ch.save().then(function(c) { d.resolve([c]); }).fail(d.reject);
					}).fail(d.reject);
				} else {
					d.resolve(x);
				}
			});
			return d.promise();
		};
		$scope.addFollowing = function(url) {
			var id = 'following-'+url;
			box.obj(id).set({url:url, followed:new Date(), type:'microfollow'}).save();
			return true;
		};
		$scope.addPost = function(body) {
			var id = 'timon-post-'+u.guid(),
				username = $scope.login.username,
				name=  $scope.name,
				d = u.deferred();
			box.obj($scope.login.username).then(function(author) {
				if (!author.peek('name')) {
					author.set({name:name});
					author.save();
				}
				box.obj(id).set({body:body, author:author, created: new Date(), type:'micropost'})
					.save()
					.then(d.resolve)
					.fail(function(err) { console.error('error posting tweet', err); d.reject(); });
			}).fail(function(err) { console.error('error getting author ', err); d.reject(); });

			// return d.promise();
			return true;
		};
		$scope.deletePost = function(m) {
			delete $scope.timeline[m.id];
			m.destroy().then(function(d) {
				console.info('deleted ', m.id);
			}).fail(function(err) { console.error('failed to delete', m.id, err); });
		};
		$scope.clearInput = function(name) {
			$scope.input[name] = '';
			return true;
		};
		$scope.closeAddFollowing = function() {
			$("#addFollowingModal").modal('hide');
		};
		$scope.deleteFollowing = function(m) {
			m.destroy();
			sa(function() { delete $scope.following[m.peek('url')] });
		};

		$scope.$watch('selected_box', function(boxid) {	initialise(boxid); });

		window.$s = $scope;
		window.store = store;
		window.xfn = transformers;
	});

<commit_msg>each post now has reference to channel<commit_after>/* jshint undef: true, strict:false, trailing:false, unused:false, -W110 */
/* global require, exports, console, process, module, describe, it, expect, jasmine, angular, _, $ */
angular.module('timon',['indx', 'ngAnimate'])
	.controller('main', function($scope, client, utils, transformers) {
		var store = client.store,
			u = utils, box, diffQs = [],
			sa = function(fn) { return u.safeApply($scope, fn); },
			guid = u.guid(),
			get_user = function(store) {
				var d = u.deferred();
				store.checkLogin().then(function(l) {
					if (l && l.user_metadata) {
						var um = JSON.parse(l.user_metadata);
						if (um && um.name) { return d.resolve(l, um.name); }
					}
					d.resolve(l, l.username);
				}).fail(function(err) { console.error(err); d.reject(); });
				return d.promise();
			};

		$scope._ = _;
		$scope.input = {};

		var initialise = function(boxid) {
			// kill previous queries
			if (!(boxid && boxid.length > 0)) { return ; }

			$scope.followers = [];
			$scope.following = {};
			$scope.channels = {};
			$scope.timeline = {};
			$scope.people = [];
			$scope.test = {};

			diffQs.map(function(q) { q.stop(); });
			diffQs = [];

			store.getBox(boxid).then(function(b) {
				window.b = b;
				box = b;
				b.standingQuery({ type:'post' }, function(message) {
					sa(function() { $scope.timeline[message.id] = message; });
				}).then(function(diffid) { diffQs.push(diffid); }).fail(function(err) {
					console.error('error setting up standing query for following ', err);
				});
				b.standingQuery({ type:'follow' }, function(following) {
					sa(function() { $scope.following[following.peek('url')] = following; });
				}).then(function(diffid) { diffQs.push(diffid); }).fail(function(err) {
					console.error('error setting up standing query for following ', err);
				});
				b.standingQuery({ type:'channel' }, function(cobj) {
					sa(function() { $scope.channels[cobj.id] = cobj; });
				}).then(function(diffid) { diffQs.push(diffid); }).fail(function(err) {
					console.error('error setting up standing query for following ', err);
				});
				get_user(store).then(function(login, name) {
					createChannel(b,login,name);
					$scope.login = login;
					$scope.name = name;
				});
			}).fail(function(err) {  console.error('error getting box ', err); });

		};
		var getChannelURL = function(store,box,id) {
			var url = [store._getBaseURL(), box.id, id].join('/');
			console.info('channel url ', url);
			return url;
		};
		var createChannel = function(box, ownerlogin, ownername) {
			var d = u.deferred();
			box.query({type:'channel'}).then(function(x) {
				if (x.length == 0) {
					var id = 'channel-main';
					// console.log(' no channels ');
					box.obj([id, ownerlogin.username]).then(function(objs) {
						var ch = objs[0], user = objs[1];
						ch.set({type:'channel', owner:user, ownername:ownername, created: new Date(), url:getChannelURL(store,box,id)});
						ch.save().then(function(c) { d.resolve([c]); }).fail(d.reject);
					}).fail(d.reject);
				} else {
					d.resolve(x);
				}
			});
			return d.promise();
		};
		$scope.addFollowing = function(url) {
			var id = 'following-'+url;
			box.obj(id).set({url:url, followed:new Date(), type:'follow'}).save();
			return true;
		};
		$scope.addPost = function(body, channel) {
			var id = 'timon-post-'+u.guid(),
				username = $scope.login.username,
				name=  $scope.name,
				d = u.deferred();
			channel = channel ? channel : $scope.channels['channel-main'];
			box.obj($scope.login.username).then(function(author) {
				if (!author.peek('name')) {
					author.set({name:name});
					author.save();
				}
				box.obj(id).set({body:body, author:author, created: new Date(), type:'post', channel:channel})
					.save()
					.then(d.resolve)
					.fail(function(err) { console.error('error posting tweet', err); d.reject(); });
			}).fail(function(err) { console.error('error getting author ', err); d.reject(); });

			// return d.promise();
			return true;
		};
		$scope.deletePost = function(m) {
			delete $scope.timeline[m.id];
			m.destroy().then(function(d) {
				console.info('deleted ', m.id);
			}).fail(function(err) { console.error('failed to delete', m.id, err); });
		};
		$scope.clearInput = function(name) {
			$scope.input[name] = '';
			return true;
		};
		$scope.closeAddFollowing = function() {
			$("#addFollowingModal").modal('hide');
		};
		$scope.deleteFollowing = function(m) {
			m.destroy();
			sa(function() { delete $scope.following[m.peek('url')] });
		};

		$scope.$watch('selected_box', function(boxid) {	initialise(boxid); });

		window.$s = $scope;
		window.store = store;
		window.xfn = transformers;
	});

<|endoftext|>


<commit_before>import React from 'react';

const MarkItem = ({ item, search }) => {
	let { index, word } = item;

	if (!search) {
		return { word };
	}

	if (index === -1) {
		return { word };
	}

	let searchLength = search.length;

	return (

				{ word.substr(0, index) }
				{ word.substr(index, searchLength) }
				{ word.substr(index+searchLength, item.length) }

	);
};

MarkItem.propTypes = {
	item: React.PropTypes.shape({
		index: React.PropTypes.number.isRequired,
		word: React.PropTypes.string.isRequired
	}).isRequired,
	search: React.PropTypes.string.isRequired
};

export default MarkItem;<commit_msg>MarkItem refactor<commit_after>import React from 'react';

const MarkItem = ({ item, search }) => {
	let { index, word } = item;

	if (!search || index === -1) {
		return { word };
	}

	let length = search.length;

	return (

				{ word.substr(0, index) }
				{ word.substr(index, length) }
				{ word.substr(index + length, word.length) }

	);
};

MarkItem.propTypes = {
	item: React.PropTypes.shape({
		index: React.PropTypes.number.isRequired,
		word: React.PropTypes.string.isRequired
	}).isRequired,
	search: React.PropTypes.string.isRequired
};

export default MarkItem;<|endoftext|>


<commit_before>'use strict';

var SNIPPET_METRICS_SAMPLE_RATE = {{ settings.METRICS_SAMPLE_RATE }};
var SNIPPET_METRICS_URL = '{{ metrics_url }}';
var ABOUTHOME_SHOWN_SNIPPET = null;
var USER_COUNTRY = null;
var GEO_CACHE_DURATION = 1000 * 60 * 60 * 24 * 30; // 30 days

(function() {
    'use strict';

    if (ABOUTHOME_SNIPPETS.length > 0) {
        ABOUTHOME_SHOWN_SNIPPET = chooseSnippet(ABOUTHOME_SNIPPETS);
    }

    if (ABOUTHOME_SHOWN_SNIPPET === null) {
        return;
    }

    // Inject the snippet onto the page.
    var snippetElement = document.createElement('div');
    snippetElement.innerHTML = ABOUTHOME_SHOWN_SNIPPET.code;
    document.getElementById('snippets').appendChild(snippetElement);
    activateScriptTags(snippetElement);
    document.querySelector("#snippets-container").style.display = "block";

    // By the time show_snippet event reaches #snippets the snippet
    // will have finished initializing and altering the DOM. It's the
    // time to modifyLinks() and addSnippetBlockLinks().
    var snippets = document.getElementById('snippets');
    snippets.addEventListener('show_snippet', function(event) {
        var snippetsContainer = document.getElementById('snippets-container');
        // Add sample rate and snippet ID to currently displayed links.
        var parameters = ('sample_rate=' + SNIPPET_METRICS_SAMPLE_RATE + '&snippet_name=' +
                          ABOUTHOME_SHOWN_SNIPPET.id);
        modifyLinks(snippetsContainer.querySelectorAll('a'), parameters);
        addSnippetBlockLinks(snippetsContainer.querySelectorAll('.block-snippet-button'));
        sendImpression();
    });

    // Trigger show_snippet event
    var evt = document.createEvent('Event');
    evt.initEvent('show_snippet', true, true);
    snippetElement.querySelector('.snippet').dispatchEvent(evt);

    // Fetch user country if we don't have it.
    if (!haveUserCountry()) {
        downloadUserCountry();
    }

    {% if preview %}
    function chooseSnippet(snippets) {
        return snippets[0];
    }
    {% else %}
    // Choose which snippet to display to the user based on various factors,
    // such as which country they are in.
    function chooseSnippet(snippets) {
        USER_COUNTRY = getUserCountry();
        if (USER_COUNTRY) {
            snippets = snippets.filter(
                function(snippet) {
                    var countries = snippet.countries;
                    if (countries.length && countries.indexOf(USER_COUNTRY) === -1) {
                        return false;
                    }
                    return true;
                }
            );
        }
        else {
            // If we don't have the user's country, remove all geolocated snippets.
            // Bug 1140476
            snippets = snippets.filter(
                function(snippet) {
                    return snippet.countries.length === 0;
                }
            );
        }

        // Filter Snippets based on the has_fxaccount attribute.
        var has_fxaccount = gSnippetsMap.get('appData.fxaccount');
        if (has_fxaccount === true) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_fxaccount == 'yes' ||
                            snippet.client_options.has_fxaccount == 'any');
                }
            );
        } else if (has_fxaccount === false) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_fxaccount == 'no' ||
                            snippet.client_options.has_fxaccount == 'any');
                }
            );
        } else {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_fxaccount == 'any');
                }
            );
        }

        // Filter Snippets based on TestPilot addon existence.
        var has_testpilot = Boolean(window.navigator.testpilotAddon);
        if (has_testpilot === true) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_testpilot == 'yes' ||
                            snippet.client_options.has_testpilot == 'any');
                }
            );
        } else {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_testpilot == 'no' ||
                            snippet.client_options.has_testpilot == 'any');
                }
            );
        }

        // Filter Snippets based on whether Firefox is the default browser or now.
        var appInfo = gSnippetsMap.get('appInfo');
        if (appInfo && (appInfo.defaultBrowser === true || appInfo.defaultBrowser === undefined)) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.is_default_browser == 'yes' ||
                            snippet.client_options.is_default_browser == 'any');
                }
            );
        } else {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.is_default_browser == 'no' ||
                            snippet.client_options.is_default_browser == 'any');
                }
            );
        }

        // Exclude snippets from search providers.
        var searchProvider = gSnippetsMap.get('selectedSearchEngine');
        if (searchProvider) {
            snippets = snippets.filter(
                function(snippet) {
                    var excludeFromSearchEngines = snippet.exclude_from_search_engines;
                    if (excludeFromSearchEngines.length && excludeFromSearchEngines.indexOf(searchProvider) !== -1) {
                        return false;
                    }
                    return true;
                }
            );
        }

        // Filter based on Firefox version
        var userAgent = window.navigator.userAgent.match(/Firefox\/([0-9]+)\./);
        var firefox_version  = userAgent ? parseInt(userAgent[1]) : 0;
        if (firefox_version !== 0) {
            snippets = snippets.filter(
                function(snippet) {
                    var lower_bound = snippet.client_options.version_lower_bound;
                    var upper_bound = snippet.client_options.version_upper_bound;
                    if (lower_bound && lower_bound !== 'any') {
                        if (lower_bound === 'current_release') {
                            if (firefox_version !== CURRENT_RELEASE)
                                return false;
                        }
                        else {
                            if (firefox_version < lower_bound) {
                                return false;
                            }
                        }
                    }
                    if (upper_bound && upper_bound !== 'any') {
                        if (upper_bound === 'older_than_current_release') {
                            if (firefox_version >= CURRENT_RELEASE)
                                return false;
                        }
                        else {
                            if (firefox_version > upper_bound)
                                return false;
                        }
                    }
                    return true;
                });
        }

        // Filter based on screen resolution
        var verticalScreenResolution = screen.width;
        snippets = snippets.filter(
            function(snippet) {
                let snippetResolutions = snippet.client_options.screen_resolutions.split(';');
                let display = false;
                snippetResolutions.forEach(function(resolution) {
                    let minmax = resolution.split('-');
                    if (parseInt(minmax[0]) <= verticalScreenResolution
                        && verticalScreenResolution < parseInt(minmax[1])) {
                        display = true;
                        return;
                    }
                });
                return display;
            }
        );

        // Filter based on Profile age
        //
        // profileCreatedWeeksAgo can be either undefined for Firefox
        // versions that don't expose this information, or a number >= 0.
        var profileCreatedWeeksAgo = gSnippetsMap.get('appData.profileCreatedWeeksAgo');
        if (profileCreatedWeeksAgo !== undefined) {
            snippets = snippets.filter(
                function(snippet) {
                    let lower_bound = snippet.client_options.profileage_lower_bound;
                    let upper_bound = snippet.client_options.profileage_upper_bound;
                    if (lower_bound > -1 && profileCreatedWeeksAgo < lower_bound) {
                        return false;
                    }
                    if (upper_bound > -1 && profileCreatedWeeksAgo >= upper_bound) {
                        return false;
                    }
                    return true;
                }
            );
        }
        else {
            // Remove all snippets that use profile age since this information
            // is not available.
            snippets = snippets.filter(
                function(snippet) {
                    if (snippet.client_options.profileage_lower_bound > -1
                        || snippet.client_options.profileage_upper_bound > -1) {
                        return false;
                    }
                    return true;
                }
            );
        }

        // Exclude snippets in block list.
        var blockList = getBlockList();
        snippets = snippets.filter(
            function (snippet) {
                var blockID = snippet.campaign || snippet.id;
                if (blockList.indexOf(blockID) === -1) {
                    return true;
                }
                return false;
            }
        );

        // Choose a random snippet from the snippets list.
        if (snippets && snippets.length) {
            var sum = 0;
            var number_of_snippets = snippets.length;
            for (var k = 0; k < number_of_snippets; k++) {
                sum += snippets[k].weight;
                snippets[k].weight = sum;
            }
            var random_number = Math.random() * sum;
            for (var k = 0; k < number_of_snippets; k++) {
                if (random_number < snippets[k].weight) {
                    return snippets[k];
                }
            }
        } else {
            return null;
        }
    }
    {% endif %}

    // Check whether we have the user's country stored and if it is still valid.
    function haveUserCountry() {
        // Check if there's an existing country code to use.
        if (gSnippetsMap.get('geoCountry')) {
            // Make sure we have a valid lastUpdated date.
            var lastUpdated = Date.parse(gSnippetsMap.get('geoLastUpdated'));
            if (lastUpdated) {
                // Make sure that it is past the lastUpdated date.
                var now = new Date();
                if (now < lastUpdated + GEO_CACHE_DURATION) {
                    return true;
                }
            }
        }

        return false;
    }

    function getUserCountry() {
        if (haveUserCountry()) {
            return gSnippetsMap.get('geoCountry').toLowerCase();
        } else {
            return null;
        }
    }

    // Download the user's country using the geolocation service.
    // Please do not directly use this code or Snippets key.
    // Contact MLS team for your own credentials.
    // https://location.services.mozilla.com/contact
    function downloadUserCountry() {
        var GEO_URL = "{{ settings.GEO_URL }}";
        var request = new XMLHttpRequest();
        request.timeout = 60000;
        request.onreadystatechange = function() {
            if (request.readyState == 4 && request.status == 200) {
                var country_data = JSON.parse(request.responseText);

                try {
                    gSnippetsMap.set('geoCountry', country_data.country_code);
                    gSnippetsMap.set('geoLastUpdated', new Date());
                } catch (e) {
                    // Most likely failed to load Data file. Continue on without us,
                    // we'll try again next time.
                }
            }
        };
        request.open('GET', GEO_URL, true);
        request.send();
    }

    // Notifies stats server that the given snippet ID
    // was shown. No personally-identifiable information
    // is sent.
    function sendImpression() {
        sendMetric('impression');
    }

    // Modifies the given links to include the specified GET parameters.
    function modifyLinks(links, parameters) {
        for (var k = 0, len = links.length; k < len; k++) {
            var link = links[k];
            var delimeter = (link.href.indexOf('?') !== -1 ? '&' : '?');

            // Pull the fragment off of the link
            var fragment_position = link.href.indexOf('#');
            if (fragment_position === -1) {
                fragment_position = link.href.length;
            }

            var href = link.href.substring(0, fragment_position);
            var fragment = link.href.substring(fragment_position);

            link.href = href + delimeter + parameters + fragment;
        }
    }

    // Add links to buttons that block snippets.
    function addSnippetBlockLinks(elements) {
        var blockSnippet = function (event) {
            event.preventDefault();
            event.stopPropagation();
            sendMetric('snippet-blocked');
            addToBlockList();

            // Hide #snippets-container for this tab.
            document.querySelector("#snippets-container").style.display = "none";
        };

        for (var k = 0; k < elements.length; k++) {
            var button = elements[k];
            button.addEventListener('click', blockSnippet);
        }
    }

    // Scripts injected by innerHTML are inactive, so we have to relocate them
    // through DOM manipulation to activate their contents.
    // (Adapted from http://mxr.mozilla.org/mozilla-central/source/browser/base/content/abouthome/aboutHome.js)
    function activateScriptTags(element) {
       Array.forEach(element.getElementsByTagName('script'), function(elt) {
           var relocatedScript = document.createElement('script');
           relocatedScript.type = 'application/javascript';
           relocatedScript.text = elt.text;
           elt.parentNode.replaceChild(relocatedScript, elt);
       });
    }

    // Listen for clicks on links and send metrics.
    var snippetsContainer = document.getElementById('snippets-container');
    snippetsContainer.addEventListener('click', function(event) {
        var target = event.target;
        while (target.tagName && target.tagName.toLowerCase() !== 'a') {
            // Do not track clicks outside snippets-container.
            if (target.id === 'snippets-container') {
                return;
            }
            target = target.parentNode;
        }

        // Count snippet clicks.
        if (target.dataset.eventCounted !== 'true') {
            target.dataset.eventCounted = 'true';
            // Fetch custom metric or default to 'click'
            var metric = target.dataset.metric || 'click';
            // If user is not opening a new tab, preventDefault action.
            if (target.href && (event.button === 0 && !(event.metaKey || event.ctrlKey))) {
                event.preventDefault();
                var callback = function() {
                    target.click();
                };
            }
            sendMetric(metric, callback, target.href);
        }

    }, false);

})();

// Send impressions and other interactions to the service
// If no parameter is entered, quit function
function sendMetric(metric, callback, href) {
    {# In preview mode, disable sampling, log metric, but do not send to service #}
    {% if preview %}
      console.log("[preview mode] Sending metric: " + metric);
      if (callback) {
          setTimeout(callback);
      }
      return;
    {% else %}

    if (!gSnippetsMap.get('appData.telemetryEnabled') || !SNIPPET_METRICS_URL ||
        !metric || (Math.random() > SNIPPET_METRICS_SAMPLE_RATE)) {
        if (callback) {
              // setTimeout is here because when metrics succeeds the callback is async.
              // When we don't use metrics, we should be consistent and
              // fire the callback async too.
              setTimeout(callback);
          }
          return;
      }

      var locale = '{{ locale }}';
      var userCountry = USER_COUNTRY || '';
      var campaign = ABOUTHOME_SHOWN_SNIPPET.campaign;
      var snippet_id = ABOUTHOME_SHOWN_SNIPPET.id;
      var snippet_full_name = ABOUTHOME_SHOWN_SNIPPET.name;

      var url = (SNIPPET_METRICS_URL + '?snippet_name=' + snippet_id +
                 '&snippet_full_name=' + snippet_full_name +
                 '&locale=' + locale + '&country=' + userCountry +
                 '&metric=' + metric + '&campaign=' + campaign);

      if (href) {
          url = url + "&href=" + escape(href);
      }

      var request = new XMLHttpRequest();
      request.open('GET', url);
      if (callback) {
          request.addEventListener('loadend', callback, false);
      }
      request.send();
      return request;
    {% endif %}
}

function popFromBlockList(snippetID) {
    var blockList = getBlockList();
    var item = blockList.pop(snippetID);
    gSnippetsMap.set('blockList', blockList);
    return item;
}

function addToBlockList(snippetID) {
    var blockList = getBlockList();
    if (snippetID === undefined) {
        snippetID = ABOUTHOME_SHOWN_SNIPPET.campaign || ABOUTHOME_SHOWN_SNIPPET.id;
    }
    if (blockList.indexOf(snippetID) === -1) {
        blockList = [snippetID].concat(blockList);
        gSnippetsMap.set('blockList', blockList);
    }
}

function getBlockList() {
    if (gSnippetsMap.get('blockList') === undefined) {
        gSnippetsMap.set('blockList', []);
    }
    return gSnippetsMap.get('blockList');
}
<commit_msg>Update defaultBrowser and searchEngine filtering to match new AS attribs.<commit_after>'use strict';

var SNIPPET_METRICS_SAMPLE_RATE = {{ settings.METRICS_SAMPLE_RATE }};
var SNIPPET_METRICS_URL = '{{ metrics_url }}';
var ABOUTHOME_SHOWN_SNIPPET = null;
var USER_COUNTRY = null;
var GEO_CACHE_DURATION = 1000 * 60 * 60 * 24 * 30; // 30 days

(function() {
    'use strict';

    if (ABOUTHOME_SNIPPETS.length > 0) {
        ABOUTHOME_SHOWN_SNIPPET = chooseSnippet(ABOUTHOME_SNIPPETS);
    }

    if (ABOUTHOME_SHOWN_SNIPPET === null) {
        return;
    }

    // Inject the snippet onto the page.
    var snippetElement = document.createElement('div');
    snippetElement.innerHTML = ABOUTHOME_SHOWN_SNIPPET.code;
    document.getElementById('snippets').appendChild(snippetElement);
    activateScriptTags(snippetElement);
    document.querySelector("#snippets-container").style.display = "block";

    // By the time show_snippet event reaches #snippets the snippet
    // will have finished initializing and altering the DOM. It's the
    // time to modifyLinks() and addSnippetBlockLinks().
    var snippets = document.getElementById('snippets');
    snippets.addEventListener('show_snippet', function(event) {
        var snippetsContainer = document.getElementById('snippets-container');
        // Add sample rate and snippet ID to currently displayed links.
        var parameters = ('sample_rate=' + SNIPPET_METRICS_SAMPLE_RATE + '&snippet_name=' +
                          ABOUTHOME_SHOWN_SNIPPET.id);
        modifyLinks(snippetsContainer.querySelectorAll('a'), parameters);
        addSnippetBlockLinks(snippetsContainer.querySelectorAll('.block-snippet-button'));
        sendImpression();
    });

    // Trigger show_snippet event
    var evt = document.createEvent('Event');
    evt.initEvent('show_snippet', true, true);
    snippetElement.querySelector('.snippet').dispatchEvent(evt);

    // Fetch user country if we don't have it.
    if (!haveUserCountry()) {
        downloadUserCountry();
    }

    {% if preview %}
    function chooseSnippet(snippets) {
        return snippets[0];
    }
    {% else %}
    // Choose which snippet to display to the user based on various factors,
    // such as which country they are in.
    function chooseSnippet(snippets) {
        USER_COUNTRY = getUserCountry();
        if (USER_COUNTRY) {
            snippets = snippets.filter(
                function(snippet) {
                    var countries = snippet.countries;
                    if (countries.length && countries.indexOf(USER_COUNTRY) === -1) {
                        return false;
                    }
                    return true;
                }
            );
        }
        else {
            // If we don't have the user's country, remove all geolocated snippets.
            // Bug 1140476
            snippets = snippets.filter(
                function(snippet) {
                    return snippet.countries.length === 0;
                }
            );
        }

        // Filter Snippets based on Firefox Account existence.
        var has_fxaccount = gSnippetsMap.get('appData.fxaccount');
        if (has_fxaccount === true) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_fxaccount == 'yes' ||
                            snippet.client_options.has_fxaccount == 'any');
                }
            );
        } else if (has_fxaccount === false) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_fxaccount == 'no' ||
                            snippet.client_options.has_fxaccount == 'any');
                }
            );
        } else {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_fxaccount == 'any');
                }
            );
        }

        // Filter Snippets based on TestPilot addon existence.
        var has_testpilot = Boolean(window.navigator.testpilotAddon);
        if (has_testpilot === true) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_testpilot == 'yes' ||
                            snippet.client_options.has_testpilot == 'any');
                }
            );
        } else {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.has_testpilot == 'no' ||
                            snippet.client_options.has_testpilot == 'any');
                }
            );
        }

        // Filter Snippets based on whether Firefox is the default browser or not.
        var defaultBrowser = gSnippetsMap.get('appData.defaultBrowser');
        if (defaultBrowser === true || defaultBrowser === undefined) {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.is_default_browser == 'yes' ||
                            snippet.client_options.is_default_browser == 'any');
                }
            );
        } else {
            snippets = snippets.filter(
                function(snippet) {
                    return (snippet.client_options.is_default_browser == 'no' ||
                            snippet.client_options.is_default_browser == 'any');
                }
            );
        }

        // Exclude snippets from search providers.
        var selectedSearchEngine = gSnippetsMap.get('appData.selectedSearchEngine');
        if (selectedSearchEngine) {
            snippets = snippets.filter(
                function(snippet) {
                    var excludeFromSearchEngines = snippet.exclude_from_search_engines;
                    if (excludeFromSearchEngines.length &&
                        excludeFromSearchEngines.indexOf(selectedSearchEngine.searchEngineIdentifier) !== -1) {
                        return false;
                    }
                    return true;
                }
            );
        }
        else {
            // We don't have the search engine information yet. Remove all
            // snippets with search engine filtering.
            snippets = snippets.filter(
                function(snippet) {
                    return snippet.exclude_from_search_engines.length === 0;
                }
            );
        }

        // Filter based on Firefox version
        var userAgent = window.navigator.userAgent.match(/Firefox\/([0-9]+)\./);
        var firefox_version  = userAgent ? parseInt(userAgent[1]) : 0;
        if (firefox_version !== 0) {
            snippets = snippets.filter(
                function(snippet) {
                    var lower_bound = snippet.client_options.version_lower_bound;
                    var upper_bound = snippet.client_options.version_upper_bound;
                    if (lower_bound && lower_bound !== 'any') {
                        if (lower_bound === 'current_release') {
                            if (firefox_version !== CURRENT_RELEASE)
                                return false;
                        }
                        else {
                            if (firefox_version < lower_bound) {
                                return false;
                            }
                        }
                    }
                    if (upper_bound && upper_bound !== 'any') {
                        if (upper_bound === 'older_than_current_release') {
                            if (firefox_version >= CURRENT_RELEASE)
                                return false;
                        }
                        else {
                            if (firefox_version > upper_bound)
                                return false;
                        }
                    }
                    return true;
                });
        }

        // Filter based on screen resolution
        var verticalScreenResolution = screen.width;
        snippets = snippets.filter(
            function(snippet) {
                let snippetResolutions = snippet.client_options.screen_resolutions.split(';');
                let display = false;
                snippetResolutions.forEach(function(resolution) {
                    let minmax = resolution.split('-');
                    if (parseInt(minmax[0]) <= verticalScreenResolution
                        && verticalScreenResolution < parseInt(minmax[1])) {
                        display = true;
                        return;
                    }
                });
                return display;
            }
        );

        // Filter based on Profile age
        //
        // profileCreatedWeeksAgo can be either undefined for Firefox
        // versions that don't expose this information, or a number >= 0.
        var profileCreatedWeeksAgo = gSnippetsMap.get('appData.profileCreatedWeeksAgo');
        if (profileCreatedWeeksAgo !== undefined) {
            snippets = snippets.filter(
                function(snippet) {
                    let lower_bound = snippet.client_options.profileage_lower_bound;
                    let upper_bound = snippet.client_options.profileage_upper_bound;
                    if (lower_bound > -1 && profileCreatedWeeksAgo < lower_bound) {
                        return false;
                    }
                    if (upper_bound > -1 && profileCreatedWeeksAgo >= upper_bound) {
                        return false;
                    }
                    return true;
                }
            );
        }
        else {
            // Remove all snippets that use profile age since this information
            // is not available.
            snippets = snippets.filter(
                function(snippet) {
                    if (snippet.client_options.profileage_lower_bound > -1
                        || snippet.client_options.profileage_upper_bound > -1) {
                        return false;
                    }
                    return true;
                }
            );
        }

        // Exclude snippets in block list.
        var blockList = getBlockList();
        snippets = snippets.filter(
            function (snippet) {
                var blockID = snippet.campaign || snippet.id;
                if (blockList.indexOf(blockID) === -1) {
                    return true;
                }
                return false;
            }
        );

        // Choose a random snippet from the snippets list.
        if (snippets && snippets.length) {
            var sum = 0;
            var number_of_snippets = snippets.length;
            for (var k = 0; k < number_of_snippets; k++) {
                sum += snippets[k].weight;
                snippets[k].weight = sum;
            }
            var random_number = Math.random() * sum;
            for (var k = 0; k < number_of_snippets; k++) {
                if (random_number < snippets[k].weight) {
                    return snippets[k];
                }
            }
        } else {
            return null;
        }
    }
    {% endif %}

    // Check whether we have the user's country stored and if it is still valid.
    function haveUserCountry() {
        // Check if there's an existing country code to use.
        if (gSnippetsMap.get('geoCountry')) {
            // Make sure we have a valid lastUpdated date.
            var lastUpdated = Date.parse(gSnippetsMap.get('geoLastUpdated'));
            if (lastUpdated) {
                // Make sure that it is past the lastUpdated date.
                var now = new Date();
                if (now < lastUpdated + GEO_CACHE_DURATION) {
                    return true;
                }
            }
        }

        return false;
    }

    function getUserCountry() {
        if (haveUserCountry()) {
            return gSnippetsMap.get('geoCountry').toLowerCase();
        } else {
            return null;
        }
    }

    // Download the user's country using the geolocation service.
    // Please do not directly use this code or Snippets key.
    // Contact MLS team for your own credentials.
    // https://location.services.mozilla.com/contact
    function downloadUserCountry() {
        var GEO_URL = "{{ settings.GEO_URL }}";
        var request = new XMLHttpRequest();
        request.timeout = 60000;
        request.onreadystatechange = function() {
            if (request.readyState == 4 && request.status == 200) {
                var country_data = JSON.parse(request.responseText);

                try {
                    gSnippetsMap.set('geoCountry', country_data.country_code);
                    gSnippetsMap.set('geoLastUpdated', new Date());
                } catch (e) {
                    // Most likely failed to load Data file. Continue on without us,
                    // we'll try again next time.
                }
            }
        };
        request.open('GET', GEO_URL, true);
        request.send();
    }

    // Notifies stats server that the given snippet ID
    // was shown. No personally-identifiable information
    // is sent.
    function sendImpression() {
        sendMetric('impression');
    }

    // Modifies the given links to include the specified GET parameters.
    function modifyLinks(links, parameters) {
        for (var k = 0, len = links.length; k < len; k++) {
            var link = links[k];
            var delimeter = (link.href.indexOf('?') !== -1 ? '&' : '?');

            // Pull the fragment off of the link
            var fragment_position = link.href.indexOf('#');
            if (fragment_position === -1) {
                fragment_position = link.href.length;
            }

            var href = link.href.substring(0, fragment_position);
            var fragment = link.href.substring(fragment_position);

            link.href = href + delimeter + parameters + fragment;
        }
    }

    // Add links to buttons that block snippets.
    function addSnippetBlockLinks(elements) {
        var blockSnippet = function (event) {
            event.preventDefault();
            event.stopPropagation();
            sendMetric('snippet-blocked');
            addToBlockList();

            // Hide #snippets-container for this tab.
            document.querySelector("#snippets-container").style.display = "none";
        };

        for (var k = 0; k < elements.length; k++) {
            var button = elements[k];
            button.addEventListener('click', blockSnippet);
        }
    }

    // Scripts injected by innerHTML are inactive, so we have to relocate them
    // through DOM manipulation to activate their contents.
    // (Adapted from http://mxr.mozilla.org/mozilla-central/source/browser/base/content/abouthome/aboutHome.js)
    function activateScriptTags(element) {
       Array.forEach(element.getElementsByTagName('script'), function(elt) {
           var relocatedScript = document.createElement('script');
           relocatedScript.type = 'application/javascript';
           relocatedScript.text = elt.text;
           elt.parentNode.replaceChild(relocatedScript, elt);
       });
    }

    // Listen for clicks on links and send metrics.
    var snippetsContainer = document.getElementById('snippets-container');
    snippetsContainer.addEventListener('click', function(event) {
        var target = event.target;
        while (target.tagName && target.tagName.toLowerCase() !== 'a') {
            // Do not track clicks outside snippets-container.
            if (target.id === 'snippets-container') {
                return;
            }
            target = target.parentNode;
        }

        // Count snippet clicks.
        if (target.dataset.eventCounted !== 'true') {
            target.dataset.eventCounted = 'true';
            // Fetch custom metric or default to 'click'
            var metric = target.dataset.metric || 'click';
            // If user is not opening a new tab, preventDefault action.
            if (target.href && (event.button === 0 && !(event.metaKey || event.ctrlKey))) {
                event.preventDefault();
                var callback = function() {
                    target.click();
                };
            }
            sendMetric(metric, callback, target.href);
        }

    }, false);

})();

// Send impressions and other interactions to the service
// If no parameter is entered, quit function
function sendMetric(metric, callback, href) {
    {# In preview mode, disable sampling, log metric, but do not send to service #}
    {% if preview %}
      console.log("[preview mode] Sending metric: " + metric);
      if (callback) {
          setTimeout(callback);
      }
      return;
    {% else %}

    if (!gSnippetsMap.get('appData.telemetryEnabled') || !SNIPPET_METRICS_URL ||
        !metric || (Math.random() > SNIPPET_METRICS_SAMPLE_RATE)) {
        if (callback) {
              // setTimeout is here because when metrics succeeds the callback is async.
              // When we don't use metrics, we should be consistent and
              // fire the callback async too.
              setTimeout(callback);
          }
          return;
      }

      var locale = '{{ locale }}';
      var userCountry = USER_COUNTRY || '';
      var campaign = ABOUTHOME_SHOWN_SNIPPET.campaign;
      var snippet_id = ABOUTHOME_SHOWN_SNIPPET.id;
      var snippet_full_name = ABOUTHOME_SHOWN_SNIPPET.name;

      var url = (SNIPPET_METRICS_URL + '?snippet_name=' + snippet_id +
                 '&snippet_full_name=' + snippet_full_name +
                 '&locale=' + locale + '&country=' + userCountry +
                 '&metric=' + metric + '&campaign=' + campaign);

      if (href) {
          url = url + "&href=" + escape(href);
      }

      var request = new XMLHttpRequest();
      request.open('GET', url);
      if (callback) {
          request.addEventListener('loadend', callback, false);
      }
      request.send();
      return request;
    {% endif %}
}

function popFromBlockList(snippetID) {
    var blockList = getBlockList();
    var item = blockList.pop(snippetID);
    gSnippetsMap.set('blockList', blockList);
    return item;
}

function addToBlockList(snippetID) {
    var blockList = getBlockList();
    if (snippetID === undefined) {
        snippetID = ABOUTHOME_SHOWN_SNIPPET.campaign || ABOUTHOME_SHOWN_SNIPPET.id;
    }
    if (blockList.indexOf(snippetID) === -1) {
        blockList = [snippetID].concat(blockList);
        gSnippetsMap.set('blockList', blockList);
    }
}

function getBlockList() {
    if (gSnippetsMap.get('blockList') === undefined) {
        gSnippetsMap.set('blockList', []);
    }
    return gSnippetsMap.get('blockList');
}
<|endoftext|>


<commit_before>import {getRoutes} from '../shared/routes';
import respond from './view/html-renderer';
import Root from '../shared/views/root/root';
import {configureStore} from '../shared/store/create';

const defaultPort = 3333;
const port = process.env.PORT || defaultPort;

export default {
  server: {connections: {routes: {security: true}}},
  connections: [{port}],
  registrations: [
    {plugin: 'scooter'},
    {
      plugin: {
        register: 'blankie',
        options: {
          reportOnly: true,
          reportUri: 'https://travi.report-uri.io/r/default/csp/reportOnly',
          defaultSrc: 'https:',
          styleSrc: [
            'self',
            'fonts.googleapis.com'
          ],
          scriptSrc: [
            'self',
            'cdn.polyfill.io',
            'www.google-analytics.com'
          ],
          fontSrc: [
            'data:',
            'fonts.gstatic.com'
          ]
        }
      }
    },
    {plugin: 'inert'},
    {plugin: 'vision'},
    {plugin: 'h2o2'},
    {
      plugin: {
        register: 'visionary',
        options: {
          engines: {mustache: 'hapi-mustache'},
          path: './lib/server/view'
        }
      }
    },
    {
      plugin: {
        register: 'good',
        options: {
          ops: false,
          reporters: {
            console: [
              {
                module: 'good-squeeze',
                name: 'Squeeze',
                args: [{log: '*', request: '*', response: '*', error: '*'}]
              },
              {module: 'good-console'},
              'stdout'
            ]
          }
        }
      }
    },
    {plugin: '@travi/hapi-html-request-router'},
    {
      plugin: {
        register: '@travi/hapi-react-router',
        options: {routes: getRoutes(), respond, Root, configureStore}
      }
    },
    {plugin: './core/landing'},
    {plugin: './view/static-assets'},
    {plugin: './resources/routes'},
    {plugin: './view/error-renderer'}
  ]
};
<commit_msg>fix(csp): allowed images from gravatar and google analytics<commit_after>import {getRoutes} from '../shared/routes';
import respond from './view/html-renderer';
import Root from '../shared/views/root/root';
import {configureStore} from '../shared/store/create';

const defaultPort = 3333;
const port = process.env.PORT || defaultPort;

export default {
  server: {connections: {routes: {security: true}}},
  connections: [{port}],
  registrations: [
    {plugin: 'scooter'},
    {
      plugin: {
        register: 'blankie',
        options: {
          reportOnly: true,
          reportUri: 'https://travi.report-uri.io/r/default/csp/reportOnly',
          defaultSrc: 'https:',
          styleSrc: [
            'self',
            'fonts.googleapis.com'
          ],
          scriptSrc: [
            'self',
            'cdn.polyfill.io',
            'www.google-analytics.com'
          ],
          fontSrc: [
            'data:',
            'fonts.gstatic.com'
          ],
          imgSrc: [
            'www.gravatar.com',
            'www.google-analytics.com'
          ]
        }
      }
    },
    {plugin: 'inert'},
    {plugin: 'vision'},
    {plugin: 'h2o2'},
    {
      plugin: {
        register: 'visionary',
        options: {
          engines: {mustache: 'hapi-mustache'},
          path: './lib/server/view'
        }
      }
    },
    {
      plugin: {
        register: 'good',
        options: {
          ops: false,
          reporters: {
            console: [
              {
                module: 'good-squeeze',
                name: 'Squeeze',
                args: [{log: '*', request: '*', response: '*', error: '*'}]
              },
              {module: 'good-console'},
              'stdout'
            ]
          }
        }
      }
    },
    {plugin: '@travi/hapi-html-request-router'},
    {
      plugin: {
        register: '@travi/hapi-react-router',
        options: {routes: getRoutes(), respond, Root, configureStore}
      }
    },
    {plugin: './core/landing'},
    {plugin: './view/static-assets'},
    {plugin: './resources/routes'},
    {plugin: './view/error-renderer'}
  ]
};
<|endoftext|>


<commit_before>const frisby = require('frisby')
const Joi = frisby.Joi
const config = require('config')

const REST_URL = 'http://localhost:3000/rest'

describe('/rest/track-order/:id', () => {
  it('GET tracking results for the order id', () => {
    return frisby.get(REST_URL + '/track-order/5267-f9cd5882f54c75a3')
      .expect('status', 200)
      .expect('json', {})
  })

  it('GET all orders by injecting into orderId', () => {
    var product = Joi.object().keys({
      quantity: Joi.number(),
      name: Joi.string(),
      price: Joi.number(),
      total: Joi.number()
    })
    return frisby.get(REST_URL + '/track-order/%27%20%7C%7C%20true%20%7C%7C%20%27')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data.*', {
        orderId: Joi.string(),
        email: Joi.string(),
        totalPrice: Joi.number(),
        products: Joi.array().items(product),
        eta: Joi.string(),
        _id: Joi.string()
      })
  })

  it('GET order ids get truncated after 40 characters', () => {
    return frisby.get(REST_URL + '/track-order/aaaaabbbbbcccccdddddeeeeefffffggggghhhhhiiiiijjjjj')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data.*', {
        orderId: 'aaaaabbbbbcccccdddddeeeeefffffggggghhhh...'
      })
  })

  it('GET injected code into order id raises error after being truncated to 40 characters', () => {
    return frisby.get(REST_URL + '/track-order/f%22%27%20%7C%7C%20%28function%28%29%20%7B%7B%20%7Bwhile%20%28true%29%20%7Bconsole.log%28%27endless%20loop%21%27%29%7D%7D%20%7D%7D%29%28%29%3B%20%2F%2F%22')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '<h1>' + config.get('application.name') + ' (Express')
      .expect('bodyContains', 'SyntaxError: Unexpected token ...')
  })
})
<commit_msg>Make error expectation less specific<commit_after>const frisby = require('frisby')
const Joi = frisby.Joi
const config = require('config')

const REST_URL = 'http://localhost:3000/rest'

describe('/rest/track-order/:id', () => {
  it('GET tracking results for the order id', () => {
    return frisby.get(REST_URL + '/track-order/5267-f9cd5882f54c75a3')
      .expect('status', 200)
      .expect('json', {})
  })

  it('GET all orders by injecting into orderId', () => {
    var product = Joi.object().keys({
      quantity: Joi.number(),
      name: Joi.string(),
      price: Joi.number(),
      total: Joi.number()
    })
    return frisby.get(REST_URL + '/track-order/%27%20%7C%7C%20true%20%7C%7C%20%27')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data.*', {
        orderId: Joi.string(),
        email: Joi.string(),
        totalPrice: Joi.number(),
        products: Joi.array().items(product),
        eta: Joi.string(),
        _id: Joi.string()
      })
  })

  it('GET order ids get truncated after 40 characters', () => {
    return frisby.get(REST_URL + '/track-order/aaaaabbbbbcccccdddddeeeeefffffggggghhhhhiiiiijjjjj')
      .expect('status', 200)
      .expect('header', 'content-type', /application\/json/)
      .expect('jsonTypes', 'data.*', {
        orderId: 'aaaaabbbbbcccccdddddeeeeefffffggggghhhh...'
      })
  })

  it('GET injected code into order id raises error after being truncated to 40 characters', () => {
    return frisby.get(REST_URL + '/track-order/f%22%27%20%7C%7C%20%28function%28%29%20%7B%7B%20%7Bwhile%20%28true%29%20%7Bconsole.log%28%27endless%20loop%21%27%29%7D%7D%20%7D%7D%29%28%29%3B%20%2F%2F%22')
      .expect('status', 500)
      .expect('header', 'content-type', /text\/html/)
      .expect('bodyContains', '<h1>' + config.get('application.name') + ' (Express')
      .expect('bodyContains', 'SyntaxError')
  })
})
<|endoftext|>


<commit_before>'use strict';

var chai = require('chai');
var expect = chai.expect;

var FilterJSCompiler = require('../../../lib/compiler/filters/filter-js-compiler.js');
var JuttleMoment = require('../../../lib/moment').JuttleMoment;
var Filter = require('../../../lib/runtime/filter');
var parser = require('../../../lib/parser');
var errors = require('../../../lib/errors');
var SemanticPass = require('../../../lib/compiler/semantic');

// Needed to evaluate compiled Juttle code.
var juttle = require('../../../lib/runtime/runtime');   // eslint-disable-line

function filterPoints(filter, points) {
    /* jshint evil:true */

    var ast = parser.parseFilter(filter).ast;

    // We need to run the semantic pass to convert Variable nodes to field
    // references.
    var semantic = new SemanticPass();
    ast = semantic.sa_expr(ast);

    var compiler = new FilterJSCompiler();
    var fn = eval(compiler.compile(ast));

    return points.filter(fn);
}

chai.use(function(chai, utils) {
    chai.Assertion.addMethod('filter', function(points, result) {
        var filtered = filterPoints(this._obj, points);

        this.assert(
            utils.eql(filtered, result),
            'expected #{this} to filter points to #{exp} but it filtered it to #{act}',
            'expected #{this} to not filter points to #{exp} but it did',
            result,
            filtered,
            true
        );
    });
});

describe('FilterJSCompiler', function() {
    var FILTER_AST = {
        type: 'ExpressionFilterTerm',
        expression: {
            type: 'BinaryExpression',
            operator: '<',
            left: { type: 'Variable', name: 'a' },
            right: { type: 'NumericLiteral', value: 5 }
        }
    };

    var POINTS_VALUES = [
        { a: null },
        { a: true },
        { a: false },
        { a: 5 },
        { a: Infinity },
        { a: NaN },
        { a: 'abcd' },
        { a: /abcd/ },
        { a: /abcd/gim },
        { a: new JuttleMoment('2015-01-01T00:00:05.000Z') },
        { a: JuttleMoment.duration('00:00:05.000') },
        { a: new Filter(FILTER_AST, 'a < 5') },
        { a: [ 1, 2, 3 ] },
        { a: { a: 1, b: 2, c: 3 } }
    ];

    var POINTS_NUMBERS = [
        { a: 1 },
        { a: 2 },
        { a: 3 }
    ];

    var POINTS_STRINGS = [
        { a: 'abcd' },
        { a: 'efgh' },
        { a: 'ijkl' }
    ];

    it('compiles NullLiteral correctly', function() {
        expect('a == null').to.filter(POINTS_VALUES, [ { a: null } ]);
    });

    it('compiles BooleanLiteral correctly', function() {
        expect('a == true').to.filter(POINTS_VALUES, [ { a: true } ]);
        expect('a == false').to.filter(POINTS_VALUES, [ { a: false } ]);
    });

    it('compiles NumericLiteral correctly', function() {
        expect('a == 5').to.filter(POINTS_VALUES, [ { a: 5 } ]);
    });

    it('compiles InfinityLiteral correctly', function() {
        expect('a == Infinity').to.filter(POINTS_VALUES, [ { a: Infinity } ]);

        // Negative InfinityLiteral can't be tested because "-Infinity" is
        // parsed as a unary operator and a positive InfinityLiteral.
    });

    it('compiles NaNLiteral correctly', function() {
        // No value equals NaN, not even NaN.
        expect('a == NaN').to.filter(POINTS_VALUES, []);
    });

    it('compiles StringLiteral correctly', function() {
        expect('a == "abcd"').to.filter(POINTS_VALUES, [ { a: 'abcd' } ]);
    });

    it('compiles RegularExpressionLiteral correctly', function() {
        expect('a == /abcd/').to.filter(POINTS_VALUES, [ { a: /abcd/ } ]);
        expect('a == /abcd/gim').to.filter(POINTS_VALUES, [ { a: /abcd/gim } ]);
    });

    it('compiles MomentLiteral correctly', function() {
        expect('a == :2015-01-01T00:00:05.000Z:').to.filter(
            POINTS_VALUES,
            [ { a: new JuttleMoment('2015-01-01T00:00:05.000Z') } ]
        );
    });

    it('compiles DurationLiteral correctly', function() {
        expect('a == :00:00:05.000:').to.filter(
            POINTS_VALUES,
            [ { a: new JuttleMoment.duration('00:00:05.000') } ]
        );
    });

    // FilterLiteral can't be tested because there is no filter literal syntax.

    it('compiles ArrayLiteral correctly', function() {
        expect('a == [ 1, 2, 3 ]').to.filter(POINTS_VALUES, [ { a: [ 1, 2, 3 ] } ]);
    });

    it('compiles UnaryExpression correctly', function() {
        expect('NOT a == 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a: 3 } ]);
        expect('*"a" == 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
    });

    it('compiles BinaryExpression correctly', function() {
        expect('a == 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
        expect('a != 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a: 3 } ]);
        expect('a =~ "efgh"').to.filter(POINTS_STRINGS, [ { a: 'efgh' } ]);
        expect('a !~ "efgh"').to.filter(POINTS_STRINGS, [ { a: 'abcd' }, { a: 'ijkl' } ]);
        expect('a < 2').to.filter(POINTS_NUMBERS, [ { a: 1 } ]);
        expect('a > 2').to.filter(POINTS_NUMBERS, [ { a: 3 } ]);
        expect('a <= 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a : 2 } ]);
        expect('a >= 2').to.filter(POINTS_NUMBERS, [ { a: 2 }, { a : 3 } ]);
        expect('a in [ 1, 3 ]').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a : 3 } ]);
        expect('a <= 2 AND a >= 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
        expect('a <= 2 OR a >= 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a: 2 }, { a: 3 } ]);
    });

    it('compiles ExpressionFilterTerm correctly', function() {
        expect('a == 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
    });

    it('compiles SimpleFilterTerm correctly', function() {
        expect(function() {
            filterPoints('"abcd"', POINTS_STRINGS);
        }).to.throw(errors.CompileError);

        // FilterLiteral can't be tested because there is no filter literal
        // syntax.
    });
});
<commit_msg>filter-js-compiler.spec.js: Small cleanups<commit_after>'use strict';

var chai = require('chai');
var expect = chai.expect;

var Filter = require('../../../lib/runtime/filter');
var JuttleMoment = require('../../../lib/moment').JuttleMoment;
var SemanticPass = require('../../../lib/compiler/semantic');
var _ = require('underscore');
var errors = require('../../../lib/errors');
var parser = require('../../../lib/parser');

var FilterJSCompiler = require('../../../lib/compiler/filters/filter-js-compiler.js');

// Needed to evaluate compiled filters.
var juttle = require('../../../lib/runtime/runtime');   // eslint-disable-line

function filterPoints(filter, points) {
    var ast = parser.parseFilter(filter).ast;

    // We need to run the semantic pass to convert Variable nodes to field
    // references.
    var semantic = new SemanticPass();
    ast = semantic.sa_expr(ast);

    var compiler = new FilterJSCompiler();
    var fn = eval(compiler.compile(ast));

    return _.filter(points, fn);
}

chai.use(function(chai, utils) {
    chai.Assertion.addMethod('filter', function(points, result) {
        var filtered = filterPoints(this._obj, points);

        this.assert(
            utils.eql(filtered, result),
            'expected #{this} to filter points to #{exp} but it filtered it to #{act}',
            'expected #{this} to not filter points to #{exp} but it did',
            result,
            filtered,
            true
        );
    });
});

describe('FilterJSCompiler', function() {
    var FILTER_AST = {
        type: 'ExpressionFilterTerm',
        expression: {
            type: 'BinaryExpression',
            operator: '<',
            left: { type: 'Variable', name: 'a' },
            right: { type: 'NumericLiteral', value: 5 }
        }
    };

    var POINTS_VALUES = [
        { a: null },
        { a: true },
        { a: false },
        { a: 5 },
        { a: Infinity },
        { a: NaN },
        { a: 'abcd' },
        { a: /abcd/ },
        { a: /abcd/gim },
        { a: new JuttleMoment('2015-01-01T00:00:05.000Z') },
        { a: JuttleMoment.duration('00:00:05.000') },
        { a: new Filter(FILTER_AST, 'a < 5') },
        { a: [ 1, 2, 3 ] },
        { a: { a: 1, b: 2, c: 3 } }
    ];

    var POINTS_NUMBERS = [
        { a: 1 },
        { a: 2 },
        { a: 3 }
    ];

    var POINTS_STRINGS = [
        { a: 'abcd' },
        { a: 'efgh' },
        { a: 'ijkl' }
    ];

    it('compiles NullLiteral correctly', function() {
        expect('a == null').to.filter(POINTS_VALUES, [ { a: null } ]);
    });

    it('compiles BooleanLiteral correctly', function() {
        expect('a == true').to.filter(POINTS_VALUES, [ { a: true } ]);
        expect('a == false').to.filter(POINTS_VALUES, [ { a: false } ]);
    });

    it('compiles NumericLiteral correctly', function() {
        expect('a == 5').to.filter(POINTS_VALUES, [ { a: 5 } ]);
    });

    it('compiles InfinityLiteral correctly', function() {
        expect('a == Infinity').to.filter(POINTS_VALUES, [ { a: Infinity } ]);

        // Negative InfinityLiteral can't be tested because "-Infinity" is
        // parsed as a unary operator and a positive InfinityLiteral.
    });

    it('compiles NaNLiteral correctly', function() {
        // No value equals NaN, not even NaN.
        expect('a == NaN').to.filter(POINTS_VALUES, []);
    });

    it('compiles StringLiteral correctly', function() {
        expect('a == "abcd"').to.filter(POINTS_VALUES, [ { a: 'abcd' } ]);
    });

    it('compiles RegularExpressionLiteral correctly', function() {
        expect('a == /abcd/').to.filter(POINTS_VALUES, [ { a: /abcd/ } ]);
        expect('a == /abcd/gim').to.filter(POINTS_VALUES, [ { a: /abcd/gim } ]);
    });

    it('compiles MomentLiteral correctly', function() {
        expect('a == :2015-01-01T00:00:05.000Z:').to.filter(
            POINTS_VALUES,
            [ { a: new JuttleMoment('2015-01-01T00:00:05.000Z') } ]
        );
    });

    it('compiles DurationLiteral correctly', function() {
        expect('a == :00:00:05.000:').to.filter(
            POINTS_VALUES,
            [ { a: new JuttleMoment.duration('00:00:05.000') } ]
        );
    });

    // FilterLiteral can't be tested because there is no filter literal syntax.

    it('compiles ArrayLiteral correctly', function() {
        expect('a == [ 1, 2, 3 ]').to.filter(POINTS_VALUES, [ { a: [ 1, 2, 3 ] } ]);
    });

    it('compiles UnaryExpression correctly', function() {
        expect('NOT a == 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a: 3 } ]);
        expect('*"a" == 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
    });

    it('compiles BinaryExpression correctly', function() {
        expect('a == 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
        expect('a != 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a: 3 } ]);
        expect('a =~ "efgh"').to.filter(POINTS_STRINGS, [ { a: 'efgh' } ]);
        expect('a !~ "efgh"').to.filter(POINTS_STRINGS, [ { a: 'abcd' }, { a: 'ijkl' } ]);
        expect('a < 2').to.filter(POINTS_NUMBERS, [ { a: 1 } ]);
        expect('a > 2').to.filter(POINTS_NUMBERS, [ { a: 3 } ]);
        expect('a <= 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a : 2 } ]);
        expect('a >= 2').to.filter(POINTS_NUMBERS, [ { a: 2 }, { a : 3 } ]);
        expect('a in [ 1, 3 ]').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a : 3 } ]);
        expect('a <= 2 AND a >= 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
        expect('a <= 2 OR a >= 2').to.filter(POINTS_NUMBERS, [ { a: 1 }, { a: 2 }, { a: 3 } ]);
    });

    it('compiles ExpressionFilterTerm correctly', function() {
        expect('a == 2').to.filter(POINTS_NUMBERS, [ { a: 2 } ]);
    });

    it('compiles SimpleFilterTerm correctly', function() {
        expect(function() {
            filterPoints('"abcd"', POINTS_STRINGS);
        }).to.throw(errors.CompileError);

        // FilterLiteral can't be tested because there is no filter literal
        // syntax.
    });
});
<|endoftext|>


<commit_before><commit_msg>Update scenario_1.js<commit_after><|endoftext|>