diff --git a/app/panel/actions/BlockingActions.js b/app/panel/actions/BlockingActions.js index c10542a5e..dfaaa9cc8 100644 --- a/app/panel/actions/BlockingActions.js +++ b/app/panel/actions/BlockingActions.js @@ -4,7 +4,7 @@ * Ghostery Browser Extension * https://www.ghostery.com/ * - * Copyright 2018 Ghostery, Inc. All rights reserved. + * Copyright 2019 Ghostery, Inc. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -18,26 +18,17 @@ import { UPDATE_CATEGORY_BLOCKED, UPDATE_TRACKER_BLOCKED, UPDATE_TRACKER_TRUST_RESTRICT, - TOGGLE_EXPAND_ALL, - TOGGLE_EXPAND_CATEGORY + TOGGLE_EXPAND_ALL } from '../constants/constants'; -import { sendMessageInPromise } from '../utils/msg'; /** -* Fetch blocking data from background -* @return {Object} dispatch -*/ -export function getBlockingData(tabId) { - return function (dispatch) { - return sendMessageInPromise('getPanelData', { - tabId, - view: 'blocking', - }).then((data) => { - dispatch({ - type: GET_BLOCKING_DATA, - data, - }); - }); + * Update Blocking data + * @return {Object} + */ +export function updateBlockingData(data) { + return { + type: GET_BLOCKING_DATA, + data, }; } @@ -123,15 +114,3 @@ export function toggleExpandAll(data) { data, }; } - -/** - * Called from Category.toggleCategoryTrackers - * @param {Object} data - * @return {Object} - */ -export function toggleExpandCategory(data) { - return { - type: TOGGLE_EXPAND_CATEGORY, - data, - }; -} diff --git a/app/panel/actions/PanelActions.js b/app/panel/actions/PanelActions.js index 85e86b5b2..9e4a40cd5 100644 --- a/app/panel/actions/PanelActions.js +++ b/app/panel/actions/PanelActions.js @@ -11,7 +11,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0 */ import { - GET_PANEL_DATA, GET_SUMMARY_DATA, GET_BLOCKING_DATA, + GET_PANEL_DATA, TOGGLE_CLIQZ_FEATURE, SHOW_NOTIFICATION, CLOSE_NOTIFICATION, @@ -37,36 +37,13 @@ export function toggleCliqzFeature(featureName, isEnabled) { } /** - * Fetch panel data from background, only on the initial load. Returns combined - * Panel, Summary and Blocking data as needed. - * @return {Object} dispatch + * Init / Update Panel data + * @return {Object} */ -export function getPanelData(tabId) { - return function (dispatch) { - return sendMessageInPromise('getPanelData', { - tabId, - view: 'panel', - }).then((data) => { - // On initial load, getPanelData returns combined Panel - // and Summary data and dispatches to respective reducers - dispatch({ - type: GET_PANEL_DATA, - data: data.panel, - }); - dispatch({ - type: GET_SUMMARY_DATA, - data: data.summary, - }); - // If we're in Expert view, dispatch Blocking data to reducer - if (data.blocking !== false) { - dispatch({ - type: GET_BLOCKING_DATA, - data: data.blocking, - }); - } - // send back to Panel component as promised data - return data.panel; - }); +export function updatePanelData(data) { + return { + type: GET_PANEL_DATA, + data, }; } diff --git a/app/panel/actions/RewardsActions.js b/app/panel/actions/RewardsActions.js index 9e5ec6d1a..47529aa3b 100644 --- a/app/panel/actions/RewardsActions.js +++ b/app/panel/actions/RewardsActions.js @@ -18,24 +18,16 @@ import { SET_OFFER_READ, SEND_SIGNAL } from '../constants/constants'; -import { sendMessageInPromise } from '../utils/msg'; /** - * Fetch rewards data from background - * @param {Object} tabId null - * @return {Object} dispatch + * Store rewards data fetched from background + * @param {Object} data + * @return {Object} */ -export function getRewardsData(tabId) { - return function (dispatch) { - return sendMessageInPromise('getPanelData', { - tabId, - view: 'rewards', - }).then((data) => { - dispatch({ - type: GET_REWARDS_DATA, - data, - }); - }); +export function getRewardsData(data) { + return { + type: GET_REWARDS_DATA, + data }; } @@ -75,6 +67,9 @@ export function setOfferRead(id) { }; } +// TODO the reducer calls getRewardMessage +// determine whether it would be better to simply call getRewardMessage directly where sendSignal is called +// (both since reducers are not supposed to have side effects and also because...why the extra complexity?) /** * Sends a Signal to the Cliqz Offers * @param {String} actionId the action id of the signal diff --git a/app/panel/actions/SettingsActions.js b/app/panel/actions/SettingsActions.js index 64120e46b..b2172243a 100644 --- a/app/panel/actions/SettingsActions.js +++ b/app/panel/actions/SettingsActions.js @@ -34,19 +34,14 @@ import { hashCode } from '../../../src/utils/common'; import globals from '../../../src/classes/Globals'; /** - * Fetch settings data from background - * @return {Object} dispatch - */ -export function getSettingsData() { - return function (dispatch) { - return sendMessageInPromise('getPanelData', { - view: 'settings', - }).then((data) => { - dispatch({ - type: GET_SETTINGS_DATA, - data, - }); - }); +* Save background-provided settings data +* @param {Object} data +* @return {Object} +*/ +export function getSettingsData(data) { + return { + type: GET_SETTINGS_DATA, + data }; } diff --git a/app/panel/actions/SummaryActions.js b/app/panel/actions/SummaryActions.js index 8bc44e32e..98ed5bfbc 100644 --- a/app/panel/actions/SummaryActions.js +++ b/app/panel/actions/SummaryActions.js @@ -13,6 +13,7 @@ import { GET_CLIQZ_MODULE_DATA, + GET_SUMMARY_DATA, UPDATE_TRACKER_COUNTS, UPDATE_GHOSTERY_PAUSED, UPDATE_SITE_POLICY, @@ -20,18 +21,26 @@ import { } from '../constants/constants'; import { sendMessageInPromise } from '../utils/msg'; + /** * Fetch Cliqz Modules data from background - * @return {Object} dispatch + * @return {Object} */ -export function getCliqzModuleData() { - return function (dispatch) { - return sendMessageInPromise('getCliqzModuleData').then((data) => { - dispatch({ - type: GET_CLIQZ_MODULE_DATA, - data, - }); - }); +export function getCliqzModuleData(data) { + return { + type: GET_CLIQZ_MODULE_DATA, + data + }; +} + +/** + * Update Summary data + * @return {Object} + */ +export function updateSummaryData(data) { + return { + type: GET_SUMMARY_DATA, + data, }; } diff --git a/app/panel/actions/__tests__/RewardsActions.js b/app/panel/actions/__tests__/RewardsActions.js index 4a1aea89d..bca5cc8d0 100644 --- a/app/panel/actions/__tests__/RewardsActions.js +++ b/app/panel/actions/__tests__/RewardsActions.js @@ -45,10 +45,10 @@ describe('app/panel/actions/RewardsActions.js', () => { const data = testData; const expectedPayload = { data, type: GET_REWARDS_DATA }; - return store.dispatch(rewardsActions.getRewardsData()).then(() => { - const actions = store.getActions(); - expect(actions).toEqual([expectedPayload]); - }); + store.dispatch(rewardsActions.getRewardsData(data)); + + const actions = store.getActions(); + expect(actions).toEqual([expectedPayload]); }); test('toggleOffersEnabled action should return correctly', () => { diff --git a/app/panel/actions/__tests__/SummaryActions.js b/app/panel/actions/__tests__/SummaryActions.js index a5acaab13..755e62da3 100644 --- a/app/panel/actions/__tests__/SummaryActions.js +++ b/app/panel/actions/__tests__/SummaryActions.js @@ -44,11 +44,10 @@ describe('app/panel/actions/SummaryActions.js', () => { const data = testData; const expectedPayload = { data, type: GET_CLIQZ_MODULE_DATA }; + store.dispatch(summaryActions.getCliqzModuleData(data)); - return store.dispatch(summaryActions.getCliqzModuleData()).then(() => { - const actions = store.getActions(); - expect(actions).toEqual([expectedPayload]); - }); + const actions = store.getActions(); + expect(actions).toEqual([expectedPayload]); }); test('updateTrackerCounts action should return correctly', () => { diff --git a/app/panel/components/Blocking.jsx b/app/panel/components/Blocking.jsx index 5c76d290d..c5bf1f3c1 100644 --- a/app/panel/components/Blocking.jsx +++ b/app/panel/components/Blocking.jsx @@ -16,6 +16,7 @@ import Categories from './Blocking/Categories'; import BlockingHeader from './Blocking/BlockingHeader'; import NotScanned from './BuildingBlocks/NotScanned'; import { updateSummaryBlockingCount } from '../utils/blocking'; + /** * @class Implement Blocking View in the right * pane of the detailed (expert) panel. @@ -24,11 +25,13 @@ import { updateSummaryBlockingCount } from '../utils/blocking'; class Blocking extends React.Component { constructor(props) { super(props); + this.state = { blockingClasses: '', - disableBlocking: false, + disableBlocking: false }; } + /** * Lifecycle event */ @@ -36,25 +39,25 @@ class Blocking extends React.Component { this.updateBlockingClasses(this.props); this.updateSiteNotScanned(this.props); } + /** * Lifecycle event */ componentDidMount() { - // We only need to fetch blocking data directly on instances where the user swtiches between - // simple and expert view. Otherwise, it's fetched via Panel. Here, we check for properties that - // are returned by PanelData::blockingView() - if (this.props.categories.length === 0 && typeof this.props.toggle_individual_trackers === 'undefined') { - this.props.actions.getBlockingData(); - } + this.uiPort = chrome.runtime.connect({ name: 'blockingUIPort' }); + this.uiPort.onMessage.addListener((msg) => { + this.props.actions.updateBlockingData(msg); + }); } + /** * Lifecycle event */ componentWillReceiveProps(nextProps) { - // triggered by update to the redux store this.updateBlockingClasses(nextProps); this.updateSiteNotScanned(nextProps); } + /** * Lifecycle event */ @@ -71,6 +74,14 @@ class Blocking extends React.Component { const smartBlock = this.props.smartBlockActive && this.props.smartBlock || { blocked: {}, unblocked: {} }; updateSummaryBlockingCount(this.props.categories, smartBlock, this.props.actions.updateTrackerCounts); } + + /** + * Lifecycle event + */ + componentWillUnmount() { + this.uiPort.disconnect(); + } + /** * Filter trackers by category, or reset filters. Trigger action. * @param {string} filterName @@ -97,6 +108,7 @@ class Blocking extends React.Component { this.props.actions.updateCategories(updated_categories); } + /** * Filter trackers in categories to show only * those that are blocked. Trigger action. @@ -120,6 +132,7 @@ class Blocking extends React.Component { this.props.actions.updateCategories(updated_categories); } + /** * Filter trackers in categories to show only * those that have warnings. Trigger action. @@ -143,6 +156,7 @@ class Blocking extends React.Component { this.props.actions.updateCategories(updated_categories); } + /** * Filter trackers in categories to show only those * that have compatibility warnings. Trigger action. @@ -166,6 +180,7 @@ class Blocking extends React.Component { this.props.actions.updateCategories(updated_categories); } + /** * Filter trackers in categories to show only those that * have slow/insecure warnings. Trigger action. @@ -189,6 +204,7 @@ class Blocking extends React.Component { this.props.actions.updateCategories(updated_categories); } + /** * Set dynamic classes on .blocking-trackers. Set state. * @param {Object} props @@ -202,6 +218,7 @@ class Blocking extends React.Component { this.setState({ blockingClasses: classes.join(' ') }); } + /** * Disable controls for a site that cannot be scanned by Ghostery. Set state. * @param {Object} props nextProps @@ -216,6 +233,7 @@ class Blocking extends React.Component { this.setState({ disableBlocking: false }); } } + /** * Filter tracker list based on filter data received from props. */ @@ -240,23 +258,33 @@ class Blocking extends React.Component { this.setShow(filter.name); } } + /** * Render blocking panel. * @return {ReactComponent} ReactComponent instance */ render() { - const { categories } = this.props; + const { + actions, + categories, + expand_all_trackers, + paused_blocking, + sitePolicy, + smartBlock, + smartBlockActive + } = this.props; + return (
{(this.state.disableBlocking && this.props.is_expanded) ? @@ -264,14 +292,15 @@ class Blocking extends React.Component {
{ categories.length > 0 && }
diff --git a/app/panel/components/Blocking/Categories.jsx b/app/panel/components/Blocking/Categories.jsx index bc5110f88..ae37b7e96 100644 --- a/app/panel/components/Blocking/Categories.jsx +++ b/app/panel/components/Blocking/Categories.jsx @@ -13,6 +13,7 @@ import React from 'react'; import Category from './Category'; + /** * @class Implement Categories component, which represents a * container of available categories. This component is shared @@ -21,17 +22,19 @@ import Category from './Category'; */ class Categories extends React.Component { componentDidMount() {} + /** * Render a list of categories. Pass globalBlocking flag to all categories * in the list, so that they would know which view they are part of. * @return {ReactComponent} ReactComponent instance */ render() { - const { categories } = this.props; + const { categories, expandAll } = this.props; const globalBlocking = !!this.props.globalBlocking; const filtered = !!this.props.filtered; const categoryList = categories.map((cat, index) => ( ({ + isExpanded: !state.isExpanded + })); } + /** * Implement handler for clicking on the category block/unblock icon. * Trigger action which will block/unblock all trackers in the category. @@ -134,16 +141,18 @@ class Category extends React.Component { }); } } + /** * Update showTrackers state attribute with the value coming from nextProps. * Called in lifecycle events. - * @param {boolean} category expanded state + * @param {boolean} global expanded state */ - updateCategoryExpanded(expanded) { - if (expanded !== this.state.showTrackers) { - this.setState({ showTrackers: expanded }); + updateCategoryExpanded(expandAll) { + if (expandAll !== this.props.expandAll && expandAll !== this.state.isExpanded) { + this.setState({ isExpanded: expandAll }); } } + /** * Render a list of categories. Pass globalBlocking flag to all trackers * in the category so that they would know which view they are part of. @@ -231,7 +240,7 @@ class Category extends React.Component {
{ - category.expanded && + this.state.isExpanded && d.value); + + this.prepareDonutContainer(isSmall); + this.bakeDonut(categories, { renderRedscale, - renderGreyscale, - totalCount, - isSmall, + renderGreyscale }); } @@ -92,23 +102,75 @@ class DonutGraph extends React.Component { renderRedscale, renderGreyscale, ghosteryFeatureSelect, - isSmall, + isSmall } = this.props; - if (categories.length !== nextProps.categories.length || + if (isSmall !== nextProps.isSmall || renderRedscale !== nextProps.renderRedscale || renderGreyscale !== nextProps.renderGreyscale || - ghosteryFeatureSelect !== nextProps.ghosteryFeatureSelect || - isSmall !== nextProps.isSmall) { - this.generateGraph(nextProps.categories, { - renderRedscale: nextProps.renderRedscale, - renderGreyscale: nextProps.renderGreyscale, - totalCount: nextProps.totalCount, - isSmall: nextProps.isSmall, - }); + ghosteryFeatureSelect !== nextProps.ghosteryFeatureSelect + ) { + this.prepareDonutContainer(nextProps.isSmall); + this.nextPropsDonut(nextProps); + return; + } + + // componentWillReceiveProps gets called many times during page load as new trackers are found + // so only compare tracker totals if we don't already have to redraw anyway as a result of the cheaper checks above + const trackerTotal = categories.reduce((total, category) => total + category.num_total, 0); + const nextTrackerTotal = nextProps.categories.reduce((total, category) => total + category.num_total, 0); + if (trackerTotal !== nextTrackerTotal) { + this.nextPropsDonut(nextProps); } } + /** + * Helper function that calculates domain value for greyscale / redscale rendering + */ + getTone(catCount, catIndex) { + return catCount > 1 ? 100 / (catCount - 1) * catIndex * 0.01 : 0; + } + + /** + * Helper to retrieve a category's tooltip from the DOM + */ + grabTooltip(d) { + return document.getElementById(`${d.data.id}_tooltip`); + } + + /** + * Helper function that updates donut with nextProps values + */ + nextPropsDonut(nextProps) { + this.bakeDonut(nextProps.categories, { + renderRedscale: nextProps.renderRedscale, + renderGreyscale: nextProps.renderGreyscale, + isSmall: nextProps.isSmall, + }); + } + + /** + * Initialize the SVG element in which the donut is rendered + * Called when the component is mounted and when the size of the donut changes + * @param {boolean} isSmall are we drawing the small Detailed View donut or the bigger Simple View donut? + */ + prepareDonutContainer(isSmall) { + const size = isSmall ? 94 : 120; + this.donutRadius = size / 2; + + select(this.node).selectAll('*').remove(); + this.chart = select(this.node) + .append('svg') + .attr('class', 'donutSvg') + .attr('width', '100%') + .attr('height', '100%') + .attr('viewBox', `0 0 ${size} ${size}`) + .attr('preserveAspectRatio', 'xMinYMin'); + this.chartCenter = this.chart + .append('g') + .attr('transform', `translate(${this.donutRadius}, ${this.donutRadius})`); + } + /** * Generate donut-shaped graph with the scanning results. * Add mouse event listeners to the arcs of the donut graph that filter the @@ -116,40 +178,39 @@ class DonutGraph extends React.Component { * @param {Array} categories list of categories detected on the site * @param {Object} options options for the graph */ - generateGraph(categories, options) { + bakeDonut = throttle(this._bakeDonut.bind(this), 600, { leading: true, trailing: true }) // matches panelData#updatePanelUI throttling + + _bakeDonut(categories, options) { const { renderRedscale, renderGreyscale, isSmall } = options; const graphData = []; - const size = isSmall ? 94 : 120; - const width = +size; - const height = +size; - const radius = Math.min(width, height) / 2; - const animationDuration = categories.length > 0 ? 750 : 0; - const delays = []; + const animationDuration = categories.length > 0 ? 500 : 0; + const categoryCount = categories.length; // Process categories into graphData - if (categories.length === 0) { + if (categoryCount === 0) { graphData.push({ id: null, name: null, - value: 1, + value: 1 }); } else { - categories.forEach((category) => { + categories.forEach((cat) => { graphData.push({ - id: category.id, - name: category.name, - value: category.num_total, + id: cat.id, + name: cat.name, + value: cat.num_total }); }); graphData.sort((a, b) => a.value < b.value); } - // Clear graph - select(this.node).selectAll('*').remove(); + const trackerArc = arc() + .innerRadius(this.donutRadius - 13) + .outerRadius(this.donutRadius); // Clear tooltips categories.forEach((cat) => { @@ -159,37 +220,37 @@ class DonutGraph extends React.Component { } }); - // Draw graph - const chart = select(this.node) - .append('svg') - .attr('class', 'donutSvg') - .attr('width', '100%') - .attr('height', '100%') - .attr('viewBox', `0 0 ${width} ${height}`) - .attr('preserveAspectRatio', 'xMinYMin') - .append('g') - .attr('transform', `translate(${width / 2}, ${height / 2})`); - const trackerArc = arc() - .innerRadius(radius - 13) - .outerRadius(radius); - const trackerPie = pie() - .startAngle(-Math.PI) - .endAngle(Math.PI) - .sort(null) - .value(d => d.value); - const g = chart.selectAll('.arc') - .data(trackerPie(graphData)) - .enter().append('g') - .attr('class', 'arc'); + // CONNECT NEW DATA + const arcs = this.chartCenter.selectAll('g') + .data(this.trackerPie(graphData), d => d.data.id); + + // UPDATE + arcs.select('path') + .transition() + .duration(animationDuration) + .attrTween('d', (d) => { + const { id: catId } = d.data; + const lerpStartAngle = interpolate(this._startAngles.get(catId), d.startAngle); + const lerpEndAngle = interpolate(this._endAngles.get(catId), d.endAngle); + this._startAngles.set(catId, d.startAngle); + this._endAngles.set(catId, d.endAngle); + + return function (t) { + d.startAngle = lerpStartAngle(t); + d.endAngle = lerpEndAngle(t); + return trackerArc(d); + }; + }); - g.append('path') + // ENTER + arcs.enter().append('g') + .attr('class', 'arc') + .append('path') .style('fill', (d, i) => { if (renderGreyscale) { - const greyTone = graphData.length > 1 ? 100 / (graphData.length - 1) * i * 0.01 : 0; - return this.colors.greyscale(greyTone); + return this.colors.greyscale(this.getTone(categoryCount, i)); } else if (renderRedscale) { - const redTone = graphData.length > 1 ? 100 / (graphData.length - 1) * i * 0.01 : 0; - return this.colors.redscale(redTone); + return this.colors.redscale(this.getTone(categoryCount, i)); } return this.colors.regular(d.data.id); }) @@ -200,9 +261,10 @@ class DonutGraph extends React.Component { return 'disabled'; }) .on('mouseover', (d) => { - const pX = trackerArc.centroid(d)[0] + (width / 2); - const pY = trackerArc.centroid(d)[1] + (height / 2); - const tooltip = document.getElementById(`${d.data.id}_tooltip`); + const centroid = trackerArc.centroid(d); + const pX = centroid[0] + this.donutRadius; + const pY = centroid[1] + this.donutRadius; + const tooltip = this.grabTooltip(d); if (tooltip) { tooltip.style.left = `${pX - (tooltip.offsetWidth / 2)}px`; tooltip.style.top = `${pY - (tooltip.offsetHeight + 8)}px`; @@ -210,7 +272,7 @@ class DonutGraph extends React.Component { } }) .on('mouseout', (d) => { - const tooltip = document.getElementById(`${d.data.id}_tooltip`); + const tooltip = this.grabTooltip(d); if (tooltip) { tooltip.classList.remove('show'); } @@ -221,22 +283,12 @@ class DonutGraph extends React.Component { } }) .transition() - .duration((d) => { - const delay = d.value / graphData.reduce((sum, j) => sum + j.value, 0) * animationDuration; - delays.push(delay); - return delay; - }) - .delay((d, i) => { - if (i === 0) { return 0; } - - let sum = 0; - delays.forEach((val, j) => { - if (j < i) { sum += val; } - }); - - return sum; - }) + .duration(animationDuration) .attrTween('d', (d) => { + const { id: catId } = d.data; + this._startAngles.set(catId, d.startAngle); + this._endAngles.set(catId, d.endAngle); + const i = interpolate(d.startAngle, d.endAngle); return function (t) { d.endAngle = i(t); diff --git a/app/panel/components/Panel.jsx b/app/panel/components/Panel.jsx index 8a0eef9e2..5653176af 100644 --- a/app/panel/components/Panel.jsx +++ b/app/panel/components/Panel.jsx @@ -14,6 +14,8 @@ import React from 'react'; import Header from '../containers/HeaderContainer'; import { sendMessage } from '../utils/msg'; +import { setTheme } from '../utils/utils'; + /** * @class Implement base view with functionality common to all views. * @memberof PanelClasses @@ -26,33 +28,59 @@ class Panel extends React.Component { this.closeNotification = this.closeNotification.bind(this); this.clickReloadBanner = this.clickReloadBanner.bind(this); this.filterTrackers = this.filterTrackers.bind(this); + + this.dataInitialized = false; } /** * Lifecycle event */ componentDidMount() { sendMessage('ping', 'engaged'); - this.props.actions.getPanelData().then((data) => { - if (data.is_expert) { - // load Detail component - this.props.history.push('/detail'); - } - - // persist whitelist/blacklist/paused_blocking notifications in the event that the - // panel is openend without a page reload. - if (Object.keys(data.needsReload.changes).length) { - this.props.actions.showNotification({ - updated: 'init', - reload: true, - }); - } - if (data.enable_offers && data.unread_offer_ids.length > 0) { - sendMessage('ping', 'engaged_offer'); + this.uiPort = chrome.runtime.connect({ name: 'panelUIPort' }); + this.uiPort.onMessage.addListener((msg) => { + if (!this.dataInitialized) { + this.dataInitialized = true; + + const { panel, summary, blocking } = msg; + + const { current_theme, account } = panel; + setTheme(document, current_theme, account); + + this.props.actions.updatePanelData(panel); + this.props.actions.updateSummaryData(summary); + if (blocking) { this.props.actions.updateBlockingData(blocking); } + + if (panel.is_expert) { + // load Detail component + this.props.history.push('/detail'); + } + + // persist whitelist/blacklist/paused_blocking notifications in the event that the + // panel is opened without a page reload + if (Object.keys(panel.needsReload.changes).length) { + this.props.actions.showNotification({ + updated: 'init', + reload: true + }); + } + + if (panel.enable_offers && panel.unread_offer_ids.length > 0) { + sendMessage('ping', 'engaged_offer'); + } + } else { + this.props.actions.updatePanelData(msg); } }); } + /** + * Lifecycle event + */ + componentWillUnmount() { + this.uiPort.disconnect(); + } + /** * Close banner notification * @param {Object} event diff --git a/app/panel/components/Rewards.jsx b/app/panel/components/Rewards.jsx index 72da7171c..39d729d8d 100644 --- a/app/panel/components/Rewards.jsx +++ b/app/panel/components/Rewards.jsx @@ -15,7 +15,7 @@ import React from 'react'; import ClassNames from 'classnames'; import { Link, Route } from 'react-router-dom'; import { ToggleSlider, RewardListItem, RewardDetail } from './BuildingBlocks'; -import { sendMessage, sendRewardMessage } from '../utils/msg'; +import { sendMessage } from '../utils/msg'; import globals from '../../../src/classes/Globals'; const IS_EDGE = (globals.BROWSER_INFO.name === 'edge'); @@ -48,9 +48,11 @@ class Rewards extends React.Component { * Lifecycle event */ componentDidMount() { - this.props.actions.getRewardsData(); + this.uiPort = chrome.runtime.connect({ name: 'rewardsUIPort' }); + this.uiPort.onMessage.addListener((msg) => { + this.props.actions.getRewardsData(msg); + }); this.props.actions.sendSignal('hub_open'); - chrome.runtime.connect({ name: 'rewardsPanelPort' }); } /** @@ -89,7 +91,7 @@ class Rewards extends React.Component { componentWillUnmount() { /* @TODO send message to background to remove port onDisconnect event */ this.props.actions.sendSignal('hub_closed'); - sendRewardMessage('removeDisconnectListener'); + this.uiPort.postMessage({ name: 'RewardsComponentWillUnmount' }); } /** diff --git a/app/panel/components/Settings.jsx b/app/panel/components/Settings.jsx index 966b2dd66..8b2437047 100644 --- a/app/panel/components/Settings.jsx +++ b/app/panel/components/Settings.jsx @@ -47,11 +47,22 @@ class Settings extends React.Component { componentWillMount() { this.props.history.push('/settings/globalblocking'); } + /** * Lifecycle event. Triggers action which delivers settings data. */ componentDidMount() { - this.props.actions.getSettingsData(); + this.uiPort = chrome.runtime.connect({ name: 'settingsUIPort' }); + this.uiPort.onMessage.addListener((msg) => { + this.props.actions.getSettingsData(msg); + }); + } + + /** + * Lifecycle event + */ + componentWillUnmount() { + this.uiPort.disconnect(); } GlobalBlockingComponent = () => (); diff --git a/app/panel/components/Summary.jsx b/app/panel/components/Summary.jsx index 8eb6368e2..808258eb6 100644 --- a/app/panel/components/Summary.jsx +++ b/app/panel/components/Summary.jsx @@ -72,7 +72,14 @@ class Summary extends React.Component { * Lifecycle event */ componentDidMount() { - this.props.actions.getCliqzModuleData(); + this.uiPort = chrome.runtime.connect({ name: 'summaryUIPort' }); + this.uiPort.onMessage.addListener((msg) => { + if (msg.adblock || msg.antitracking) { + this.props.actions.getCliqzModuleData(msg); + } else { + this.props.actions.updateSummaryData(msg); + } + }); } /** @@ -86,6 +93,13 @@ class Summary extends React.Component { window.document.title = `Ghostery's findings for ${this.props.pageUrl}`; } + /** + * Lifecycle event + */ + componentWillUnmount() { + this.uiPort.disconnect(); + } + /** * Calculates total tracker latency and sets it to state * @param {Object} props Summary's props, either this.props or nextProps. @@ -108,6 +122,9 @@ class Summary extends React.Component { pageLatency = (Number(timing.loadEventEnd - timing.navigationStart) / 1000).toFixed(2); } this.setState({ trackerLatencyTotal: pageLatency }); + // reset page load value if page is reloaded while panel is open + } else if (this.props.performanceData && !performanceData) { + this.setState({ trackerLatencyTotal: pageLatency }); } } diff --git a/app/panel/components/__tests__/Rewards.jsx b/app/panel/components/__tests__/Rewards.jsx index 8ceb2cf7a..81b31548f 100644 --- a/app/panel/components/__tests__/Rewards.jsx +++ b/app/panel/components/__tests__/Rewards.jsx @@ -22,6 +22,11 @@ global.t = function (str) { }; describe('app/panel/components/Rewards.jsx', () => { + beforeAll(() => { + chrome.runtime.connect.withArgs({ name: 'rewardsUIPort' }) + .returns({ name: 'rewardsUIPort', onMessage: { addListener: () => {} } }); + }); + describe('Snapshot tests with react-test-renderer', () => { test('rewards is rendered correctly when rewards is on and rewards is null', () => { const initialState = { diff --git a/app/panel/constants/constants.js b/app/panel/constants/constants.js index 6819d35d3..3d019b381 100644 --- a/app/panel/constants/constants.js +++ b/app/panel/constants/constants.js @@ -4,7 +4,7 @@ * Ghostery Browser Extension * https://www.ghostery.com/ * - * Copyright 2018 Ghostery, Inc. All rights reserved. + * Copyright 2019 Ghostery, Inc. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -44,7 +44,6 @@ export const UPDATE_TRACKER_BLOCKED = 'UPDATE_TRACKER_BLOCKED'; export const UPDATE_TRACKER_TRUST_RESTRICT = 'UPDATE_TRACKER_TRUST_RESTRICT'; export const UPDATE_BLOCK_ALL_TRACKERS = 'UPDATE_BLOCK_ALL_TRACKERS'; export const TOGGLE_EXPAND_ALL = 'TOGGLE_EXPAND_ALL'; -export const TOGGLE_EXPAND_CATEGORY = 'TOGGLE_EXPAND_CATEGORY'; // settings export const IMPORT_SETTINGS_DIALOG = 'IMPORT_SETTINGS_DIALOG'; diff --git a/app/panel/containers/PanelContainer.js b/app/panel/containers/PanelContainer.js index 72adc7b8f..7445b83f2 100644 --- a/app/panel/containers/PanelContainer.js +++ b/app/panel/containers/PanelContainer.js @@ -16,7 +16,8 @@ import { withRouter } from 'react-router-dom'; import { bindActionCreators } from 'redux'; import Panel from '../components/Panel'; import * as panelActions from '../actions/PanelActions'; -import { filterTrackers } from '../actions/SummaryActions'; +import { filterTrackers, updateSummaryData } from '../actions/SummaryActions'; +import { updateBlockingData } from '../actions/BlockingActions'; /** * Map redux store state properties to Panel view component own properties. * @memberOf PanelContainers @@ -39,7 +40,7 @@ const mapStateToProps = state => Object.assign({}, state.panel, state.drawer, { * @return {function} to be used as an argument in redux connect call */ const mapDispatchToProps = dispatch => ({ - actions: bindActionCreators(Object.assign({}, panelActions, { filterTrackers }), dispatch), + actions: bindActionCreators(Object.assign({}, panelActions, { filterTrackers, updateSummaryData }, { updateBlockingData }), dispatch), }); /** * Connects Panel component to the Redux store. Pass updated match, location, and history props to the wrapped component. diff --git a/app/panel/reducers/__tests__/summary.js b/app/panel/reducers/__tests__/summary.js index e7e8486de..addef99fd 100644 --- a/app/panel/reducers/__tests__/summary.js +++ b/app/panel/reducers/__tests__/summary.js @@ -51,16 +51,39 @@ describe('app/panel/reducers/summary.js', () => { }); test('reducer correctly handles GET_CLIQZ_MODULE_DATA', () => { - const data = { adblock: {}, antitracking: {} }; + const data = { + adblock: { + unchangedData: false, + changedData: true, + newData: true + }, + antitracking: { + totalUnsafeCount: 3, + unchangedData: false, + changedData: true, + newData: true + } + }; const action = { data, type: GET_CLIQZ_MODULE_DATA }; - const initState = Immutable({}); - - expect(summaryReducer(initState, action)).toEqual({ - adBlock: {}, - antiTracking: { - totalUnsafeCount: 0 + const initState = Immutable({ + tab_id: 0, + adBlock: { + unchangedData: false, + changedData: false }, + antiTracking: { + totalUnsafeCount: 1, + unchangedData: false, + changedData: false + } }); + + const updatedState = Immutable.merge(initState, { + adBlock: data.adblock, + antiTracking: data.antitracking + }); + + expect(summaryReducer(initState, action)).toEqual(updatedState); }); test('reducer correctly handles UPDATE_GHOSTERY_PAUSED', () => { diff --git a/app/panel/reducers/blocking.js b/app/panel/reducers/blocking.js index d250198cc..ca8ff8162 100644 --- a/app/panel/reducers/blocking.js +++ b/app/panel/reducers/blocking.js @@ -20,10 +20,9 @@ import { UPDATE_CATEGORY_BLOCKED, UPDATE_TRACKER_BLOCKED, UPDATE_TRACKER_TRUST_RESTRICT, - TOGGLE_EXPAND_ALL, - TOGGLE_EXPAND_CATEGORY + TOGGLE_EXPAND_ALL } from '../constants/constants'; -import { updateTrackerBlocked, updateCategoryBlocked, updateBlockAllTrackers, toggleExpandAll, toggleExpandCategory } from '../utils/blocking'; +import { updateTrackerBlocked, updateCategoryBlocked, updateBlockAllTrackers, toggleExpandAll } from '../utils/blocking'; import { updateObject } from '../utils/utils'; import { sendMessage } from '../utils/msg'; @@ -77,10 +76,6 @@ export default (state = initialState, action) => { const updated = toggleExpandAll(state, action); return Object.assign({}, state, updated); } - case TOGGLE_EXPAND_CATEGORY: { - const updated = toggleExpandCategory(state, action); - return Object.assign({}, state, updated); - } case UPDATE_TRACKER_TRUST_RESTRICT: { const updated = _updateTrackerTrustRestrict(state, action); return Object.assign({}, state, updated); diff --git a/app/panel/reducers/panel.js b/app/panel/reducers/panel.js index 2da0b6cd5..c52ba5616 100644 --- a/app/panel/reducers/panel.js +++ b/app/panel/reducers/panel.js @@ -73,8 +73,6 @@ const initialState = { export default (state = initialState, action) => { switch (action.type) { case GET_PANEL_DATA: { - const { current_theme, account } = action.data; - setTheme(document, current_theme, account); return Object.assign({}, state, action.data, { initialized: true }); } case SET_THEME: { diff --git a/app/panel/reducers/summary.js b/app/panel/reducers/summary.js index 07d1278c3..80735535e 100644 --- a/app/panel/reducers/summary.js +++ b/app/panel/reducers/summary.js @@ -51,18 +51,6 @@ export default (state = initialState, action) => { return Object.assign({}, state, action.data); } case GET_CLIQZ_MODULE_DATA: { - const antiTracking = action.data.antitracking; - let totalUnsafeCount = 0; - for (const category in antiTracking) { - if (antiTracking.hasOwnProperty(category)) { - for (const app in antiTracking[category]) { - if (antiTracking[category][app] === 'unsafe') { - totalUnsafeCount++; - } - } - } - } - antiTracking.totalUnsafeCount = totalUnsafeCount; return Object.assign({}, state, { adBlock: action.data.adblock, antiTracking: action.data.antitracking }); } case UPDATE_GHOSTERY_PAUSED: { diff --git a/app/panel/utils/blocking.js b/app/panel/utils/blocking.js index 1b7815e5f..213b73f3e 100644 --- a/app/panel/utils/blocking.js +++ b/app/panel/utils/blocking.js @@ -169,24 +169,6 @@ export function toggleExpandAll(state, action) { }; } -/** - * Set property for expanding category - * @memberOf PanelUtils - * @param {Object} state current state - * @param {Object} action action which provides data - * @return {Object} object updated categories - */ -export function toggleExpandCategory(state, action) { - const { expanded } = action.data; - const updated_categories = JSON.parse(JSON.stringify(state.categories)); // deep clone - const catIndex = updated_categories.findIndex(item => item.id === action.data.cat_id); - const updated_category = updated_categories[catIndex]; - updated_category.expanded = expanded; - return { - categories: updated_categories, - }; -} - /** * Update tracker blocked / allowed status. Persist the change. * @memberOf PanelUtils diff --git a/package.json b/package.json index 757e2f23e..1983f1f2d 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "babel-core": "^7.0.0-bridge.0" }, "dependencies": { + "@cliqz/adblocker": "0.3.1", "base64-js": "^1.2.1", "browser-core": "https://s3.amazonaws.com/cdncliqz/update/edge/ghostery/v7.33/7.33.0.504d19a.tgz", "classnames": "^2.2.5", @@ -53,6 +54,7 @@ "history": "^4.7.2", "json-api-normalizer": "^0.4.10", "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", "moment": "^2.19.1", "prop-types": "^15.6.2", "query-string": "^6.1.0", diff --git a/src/background.js b/src/background.js index 4b7884b31..40846439b 100644 --- a/src/background.js +++ b/src/background.js @@ -21,10 +21,10 @@ import _ from 'underscore'; import moment from 'moment/min/moment-with-locales.min'; import cliqz, { prefs } from './classes/Cliqz'; -// object classes +// object class import Events from './classes/EventHandlers'; -import PanelData from './classes/PanelData'; // static classes +import panelData from './classes/PanelData'; import bugDb from './classes/BugDb'; import button from './classes/BrowserButton'; import c2pDb from './classes/Click2PlayDb'; @@ -56,7 +56,6 @@ window.CLIQZ = cliqz; // class instantiation const events = new Events(); -const panelData = new PanelData(); // function shortcuts const { log } = common; const { sendMessage } = utils; @@ -518,9 +517,6 @@ function handleRewards(name, message, callback) { case 'ping': metrics.ping(message); break; - case 'removeDisconnectListener': - rewards.panelPort.onDisconnect.removeListener(rewards.panelHubClosedListener); - break; case 'setPanelData': if (message.hasOwnProperty('enable_offers')) { if (!offers.isEnabled && message.enable_offers === true) { @@ -770,6 +766,7 @@ function onMessageHandler(request, sender, callback) { return handleGhosteryDotCom(name, message, tab_id); } else if (origin === 'page_performance' && name === 'recordPageInfo') { tabInfo.setTabInfo(tab_id, 'pageTiming', message.performanceAPI); + panelData.sendPageLoadTime(tab_id); return false; } else if (origin === 'notifications') { return handleNotifications(name, message, tab_id); @@ -784,21 +781,7 @@ function onMessageHandler(request, sender, callback) { } // HANDLE UNIVERSAL EVENTS HERE (NO ORIGIN LISTED ABOVE) - if (name === 'getPanelData') { - if (!message.tabId) { - utils.getActiveTab((tab) => { - const data = panelData.get(message.view, tab); - callback(data); - }); - } else { - chrome.tabs.get(+message.tabId, (tab) => { - const data = panelData.get(message.view, tab); - callback(data); - }); - } - account.getUserSettings().catch(err => log('Failed getting user settings from getPanelData:', err)); - return true; - } else if (name === 'getStats') { + if (name === 'getStats') { insights.action('getStatsTimeline', message.from, message.to, true, true).then((data) => { callback(data); }); @@ -827,36 +810,6 @@ function onMessageHandler(request, sender, callback) { return true; } callback(); - } else if (name === 'getCliqzModuleData') { - const modules = { adblock: {}, antitracking: {} }; - - const getCliqzModuleDataForTab = (tabId, callback) => { - button.update(); - if (conf.enable_ad_block) { - // update adblock count. callback() handled below based on anti-tracking status - modules.adblock = cliqz.modules.adblocker.background.actions.getAdBlockInfoForTab(tabId) || {}; - } - if (conf.enable_anti_tracking) { - cliqz.modules.antitracking.background.actions.aggregatedBlockingStats(tabId).then((data) => { - modules.antitracking = data || {}; - callback(modules); - }).catch(() => { - callback(modules); - }); - } else { - callback(modules); - } - }; - - if (message && message.tabId) { - getCliqzModuleDataForTab(+message.tabId, callback); - } else { - utils.getActiveTab((tab) => { - getCliqzModuleDataForTab(tab.id, callback); - }); - } - - return true; } else if (name === 'getTrackerDescription') { utils.getJson(message.url).then((result) => { const description = (result) ? ((result.company_in_their_own_words) ? result.company_in_their_own_words : ((result.company_description) ? result.company_description : '')) : ''; @@ -1080,10 +1033,6 @@ function initializeDispatcher() { utils.flushChromeMemoryCache(); cliqz.modules.core.action('refreshAppState'); }); - dispatcher.on('conf.save.account', () => { - // update PanelData - panelData.init(); - }); dispatcher.on('conf.save.enable_human_web', (enableHumanWeb) => { if (!IS_EDGE && !IS_CLIQZ) { setCliqzModuleEnabled(humanweb, enableHumanWeb).then(() => { @@ -1134,8 +1083,6 @@ function initializeDispatcher() { dispatcher.on('conf.changed.settings', _.debounce((key) => { log('Conf value changed for a watched user setting:', key); - // Update PanelData with new Conf properties - panelData.init(); }, 200)); dispatcher.on('globals.save.paused_blocking', () => { @@ -1619,11 +1566,20 @@ function initializeEventListeners() { // Fired when a message is sent from either an extension process (by runtime.sendMessage) or a content script (by tabs.sendMessage). onMessage.addListener(onMessageHandler); - // Fired when panel is disconnected + // These ports transmit data to panel extension components in response to + // user navigation between panel components and to changes in the background data, + // making the extension UI dynamic chrome.runtime.onConnect.addListener((port) => { - if (port && port.name === 'rewardsPanelPort') { - rewards.panelPort = port; - rewards.panelPort.onDisconnect.addListener(rewards.panelHubClosedListener); + const portNames = [ + 'blockingUIPort', + 'panelUIPort', + 'rewardsUIPort', + 'settingsUIPort', + 'summaryUIPort' + ]; + + if (portNames.includes(port.name)) { + panelData.initUIPort(port); } }); @@ -1850,8 +1806,6 @@ function initializeGhosteryModules() { ]).then(() => { // run scheduledTasks on init scheduledTasks(); - // initialize panel data - panelData.init(); }); } diff --git a/src/classes/BrowserButton.js b/src/classes/BrowserButton.js index 39f017f12..7d2aa89c3 100644 --- a/src/classes/BrowserButton.js +++ b/src/classes/BrowserButton.js @@ -15,15 +15,13 @@ import conf from './Conf'; import foundBugs from './FoundBugs'; -import cliqz from './Cliqz'; import rewards from './Rewards'; import Policy from './Policy'; +import { getCliqzAntitrackingData, getCliqzAdblockingData } from '../utils/cliqzModuleData'; import { getTab } from '../utils/utils'; import { log } from '../utils/common'; import globals from './Globals'; -const { adblocker, antitracking } = cliqz.modules; - /** * @class for handling Ghostery button. * @memberof BackgroundClasses @@ -146,12 +144,12 @@ class BrowserButton { return; } - this._getAntiTrackCount(tabId).then((antiTrackingCount) => { + getCliqzAntitrackingData(tabId).then((antitrackingData) => { const { appsCount, appsAlertCount } = this._getTrackerCount(tabId); - const adBlockingCount = this._getAdBlockCount(tabId); + const adBlockingCount = getCliqzAdblockingData(tabId).totalCount; alert = (appsAlertCount > 0); - trackerCount = (appsCount + antiTrackingCount + adBlockingCount).toString(); + trackerCount = (appsCount + antitrackingData.totalUnsafeCount + adBlockingCount).toString(); // gray-out the icon when blocking has been disabled for whatever reason if (trackerCount === '') { @@ -175,48 +173,6 @@ class BrowserButton { appsAlertCount: apps.total, }; } - - /** - * Get tracker count for Anti Tracking in a promise - * @param {number} tabId the Tab Id - * @return {Promise} the number of trackers as a Promise - */ - _getAntiTrackCount(tabId) { - return new Promise((resolve) => { - if (!conf.enable_anti_tracking || !antitracking.background) { - resolve(0); - } - antitracking.background.actions.aggregatedBlockingStats(tabId).then((antiTracking) => { - let totalUnsafeCount = 0; - for (const category in antiTracking) { - if (antiTracking.hasOwnProperty(category)) { - for (const app in antiTracking[category]) { - if (antiTracking[category][app] === 'unsafe') { - totalUnsafeCount++; - } - } - } - } - resolve(totalUnsafeCount); - }).catch(() => { - // if we encounter an error, return 0 - resolve(0); - }); - }); - } - - /** - * Get tracker count for Ad Blocking - * @param {number} tabId the Tab Id - * @return {number} the number of trackers in an object - */ - _getAdBlockCount(tabId) { - if (!conf.enable_ad_block || !adblocker.background) { - return 0; - } - const adBlocking = adblocker.background.actions.getAdBlockInfoForTab(tabId); - return adBlocking && adBlocking.totalCount || 0; - } } export default new BrowserButton(); diff --git a/src/classes/EventHandlers.js b/src/classes/EventHandlers.js index 617326c09..a17a2335c 100644 --- a/src/classes/EventHandlers.js +++ b/src/classes/EventHandlers.js @@ -23,6 +23,7 @@ import conf from './Conf'; import foundBugs from './FoundBugs'; import globals from './Globals'; import latency from './Latency'; +import panelData from './PanelData'; import Policy, { BLOCK_REASON_SS_UNBLOCKED, BLOCK_REASON_C2P_ALLOWED_THROUGH } from './Policy'; import PolicySmartBlock from './PolicySmartBlock'; import PurpleBox from './PurpleBox'; @@ -72,6 +73,8 @@ class EventHandlers { RequestsMap.clear(); this._clearTabData(tabId); this._resetNotifications(); + // TODO understand why this does not work when placed in the 'reload' branch in onCommitted + panelData.clearPageLoadTime(tabId); // initialize tabInfo, foundBugs objects for this tab tabInfo.create(tabId, url); @@ -368,10 +371,12 @@ class EventHandlers { return { cancel: false }; } + // TODO fuse this into a single call to improve performance const page_url = tabInfo.getTabInfo(tab_id, 'url'); const page_host = tabInfo.getTabInfo(tab_id, 'host'); const page_protocol = tabInfo.getTabInfo(tab_id, 'protocol'); const from_redirect = globals.REDIRECT_MAP.has(request_id); + // TODO wait to call isBug until request has passed Smart Blocking const bug_id = (page_url ? isBug(details.url, page_url) : isBug(details.url)); const processed = utils.processUrl(details.url); @@ -612,6 +617,9 @@ class EventHandlers { button.update(details.tab_id); + // throttled in PanelData + panelData.updatePanelUI(); + if (block && (conf.enable_click2play || conf.enable_click2playSocial)) { buildC2P(details, app_id); } diff --git a/src/classes/FoundBugs.js b/src/classes/FoundBugs.js index 74c09b981..6e94a7392 100644 --- a/src/classes/FoundBugs.js +++ b/src/classes/FoundBugs.js @@ -16,6 +16,40 @@ * } * } * + * this._foundApps = { + * tab_id: { + * apps: [{ + * blocked: boolean, + * cat: string, + * hasCompatibilityIssue: boolean, + * hasInsecureIssue: boolean, + * hasLatencyIssue: boolean, + * id: number, + * name: string, + * sources: [{ + * src: string, + * blocked: boolean, + * type: string + * }] + * }], + * appsMetadata: { + * appId: { + * needsCompatibilityCheck: boolean, // so we don't have to invoke fuzzyUrlMatcher more than once per app per tab + * sortingName: string, // so we don't have to lowerCase each app name each time we want to sort the apps array in getApps + * } + * }, + * appsById: { + * appId: number // index in this._foundApps[tab_id][apps] and ...[appsMetadata] + * }, + * issueCounts : { + * compatibility: number, + * insecure: number, + * latency: number, + * blocked: number + * } + * } + * } + * * Ghostery Browser Extension * https://www.ghostery.com/ * @@ -41,24 +75,52 @@ const LATENCY_ISSUE_THRESHOLD = 1000; class FoundBugs { constructor() { this._foundBugs = {}; + this._foundApps = {}; + } + + _checkForCompatibilityIssues(tab_id, tab_url) { + const { apps, appsMetadata, issueCounts } = this._foundApps[tab_id]; + apps.forEach((app) => { + const { id } = app; + if (appsMetadata[id].needsCompatibilityCheck) { + app.hasCompatibilityIssue = app.blocked ? compDb.hasIssue(id, tab_url) : false; + if (app.hasCompatibilityIssue) { issueCounts.compatibility++; } + appsMetadata[id].needsCompatibilityCheck = false; + } + }); } _ensure(tab_id) { if (!tab_id) { return false; } + if (!this._foundBugs.hasOwnProperty(tab_id)) { this._foundBugs[tab_id] = {}; } + if (!this._foundApps.hasOwnProperty(tab_id)) { + this._foundApps[tab_id] = { + apps: [], + appsMetadata: {}, + appsById: {}, + issueCounts: { + compatibility: 0, + insecure: 0, + latency: 0, + blocked: 0 + } + }; + } + return true; } /** - * Update this._foundBugs property with bug data for a tab_id + * Update this._foundBugs and this._foundApps properties with bug data for a tab_id * * Note: When called with just the tab_id parameter * (from tabs.onReplaced and webNavigation.onNavigation), this method - * is used only to initialize this._foundBugs for the tab_id + * is used only to initialize this._foundBugs and this._foundApps for the tab_id * * @param {number} tab_id tab id * @param {number} bug_id bug id @@ -75,29 +137,88 @@ class FoundBugs { return; } + const bug = this._updateFoundBugs(tab_id, bug_id, src, blocked, type); + this._updateFoundApps(tab_id, bug_id, bug); + } + + _updateFoundBugs(tab_id, bug_id, src, blocked, type) { if (!this._foundBugs[tab_id].hasOwnProperty(bug_id)) { this._foundBugs[tab_id][bug_id] = { sources: [], hasLatencyIssue: false, - hasInsecureIssue: false + hasInsecureIssue: false, + blocked: true }; } - this._foundBugs[tab_id][bug_id].sources.push({ + + const bug = this._foundBugs[tab_id][bug_id]; + + bug.sources.push({ src, blocked, type: type.toLowerCase() }); - // TODO speed this up? // Check for insecure tag loading in secure page - if (!this._foundBugs[tab_id][bug_id].hasInsecureIssue) { + if (!bug.hasInsecureIssue && !src.startsWith('https')) { const tab = tabInfo.getTabInfo(tab_id); - this._foundBugs[tab_id][bug_id].hasInsecureIssue = (tab.protocol === 'https' && !src.startsWith('https')); + bug.hasInsecureIssue = (tab.protocol === 'https'); } // once unblocked, unblocked henceforth - if (this._foundBugs[tab_id][bug_id].blocked !== false) { - this._foundBugs[tab_id][bug_id].blocked = blocked; + bug.blocked = bug.blocked && blocked; + + return bug; + } + + _updateFoundApps(tab_id, bug_id, bug) { + const { db } = bugDb; + const aid = db.bugs[bug_id].aid; // eslint-disable-line prefer-destructuring + const { + hasLatencyIssue, hasInsecureIssue, blocked, sources + } = bug; + const { + apps, appsMetadata, appsById, issueCounts + } = this._foundApps[tab_id]; + + if (appsById.hasOwnProperty(aid)) { + const app = apps[appsById[aid]]; + + if (!app.hasLatencyIssue && hasLatencyIssue) { issueCounts.latency++; } + if (!app.hasInsecureIssue && hasInsecureIssue) { issueCounts.insecure++; } + if (app.blocked && !blocked) { issueCounts.blocked--; } + + app.sources = app.sources.concat(sources); + app.hasLatencyIssue = app.hasLatencyIssue || hasLatencyIssue; + app.hasInsecureIssue = app.hasInsecureIssue || hasInsecureIssue; + app.blocked = app.blocked && blocked; + + appsMetadata[aid].needsCompatibilityCheck = + appsMetadata[aid].needsCompatibilityCheck && app.blocked; + } else { + const { name, cat } = db.apps[aid]; + + const apps_len = apps.push({ + id: aid, + name, + cat, + blocked, + sources, + hasCompatibilityIssue: false, + hasLatencyIssue, + hasInsecureIssue + }); + + if (hasLatencyIssue) { issueCounts.latency++; } + if (hasInsecureIssue) { issueCounts.insecure++; } + if (blocked) { issueCounts.blocked++; } + + appsMetadata[aid] = { + needsCompatibilityCheck: blocked, + sortingName: name.toLowerCase() + }; + + appsById[aid] = apps_len - 1; } } @@ -115,92 +236,43 @@ class FoundBugs { } /** - * Get the trackers from BugsDb that match bugs found - * on a tab_id. + * If app_id is omitted, return all the trackers we have found on this tab_id + * If app_id is provided, return this tracker if we have found it on this tab_id, or the empty array * * @param {number} tab_id tab id - * @param {boolean} sorted do we want the output tracker objects array to be sorted by tracker name? - * @param {string} tab_url tab url - * @param {number} app_id tracker id - * @return {array} array of tracker objects + * @param {boolean} sorted do we want the output tracker objects array to be sorted by tracker name? + * @param {string} tab_url tab url + * @param {number} app_id tracker id, if we are looking for a specific one + * @return {array} array of tracker object(s) */ getApps(tab_id, sorted, tab_url, app_id) { if (!this._ensure(tab_id)) { return []; } - const apps_arr = []; - const apps_obj = {}; - - const bugs = this.getBugs(tab_id); - const { db } = bugDb; - - let id; - let aid; - let latencyIssue = false; - let insecureIssue = false; - - if (!bugs) { - return bugs; + if (tab_url) { + this._checkForCompatibilityIssues(tab_id, tab_url); } - // squish all the bugs into apps first - for (id in bugs) { - if (!bugs.hasOwnProperty(id)) { - continue; - } - - aid = db.bugs[id].aid; // eslint-disable-line prefer-destructuring - if (app_id !== undefined && aid !== app_id) { - continue; - } - latencyIssue = bugs[id].hasLatencyIssue; - insecureIssue = bugs[id].hasInsecureIssue; - if (apps_obj.hasOwnProperty(aid)) { - // combine bug sources - apps_obj[aid].sources = apps_obj[aid].sources.concat(bugs[id].sources); - - if (latencyIssue) { - apps_obj[aid].hasLatencyIssue = latencyIssue; - } - - if (insecureIssue) { - apps_obj[aid].hasInsecureIssue = insecureIssue; - } + const { apps, appsMetadata } = this._foundApps[tab_id]; + const apps_arr = []; - // once unblocked, unblocked henceforth - if (apps_obj[aid].blocked !== false) { - apps_obj[aid].blocked = bugs[id].blocked; - } - } else { - apps_obj[aid] = { - id: aid, - name: db.apps[aid].name, - cat: db.apps[aid].cat, - blocked: bugs[id].blocked, - sources: bugs[id].sources, - hasCompatibilityIssue: (tab_url && bugs[id].blocked ? compDb.hasIssue(aid, tab_url) : false), - hasLatencyIssue: latencyIssue, - hasInsecureIssue: insecureIssue - }; + if (app_id) { + const { appsById } = this._foundApps[tab_id]; + if (appsById.hasOwnProperty(app_id)) { + apps_arr.push(apps[appsById[app_id]]); } - } - - // convert apps hash to array - for (id in apps_obj) { - if (apps_obj.hasOwnProperty(id)) { - apps_arr.push(apps_obj[id]); + } else { + apps_arr.push(...apps); + if (sorted) { + apps_arr.sort((a, b) => { + a = appsMetadata[a.id].sortingName; + b = appsMetadata[b.id].sortingName; + return (a > b ? 1 : (a < b ? -1 : 0)); + }); } } - if (sorted && app_id === undefined) { - apps_arr.sort((a, b) => { - a = a.name.toLowerCase(); - b = b.name.toLowerCase(); - return (a > b ? 1 : (a < b ? -1 : 0)); - }); - } - return apps_arr; } @@ -296,11 +368,11 @@ class FoundBugs { * @return {number} count of trackers */ getAppsCount(tab_id) { - const apps = this.getApps(tab_id); - if (apps) { - return apps.length; + if (!this._ensure(tab_id)) { + return 0; } - return 0; + + return this._foundApps[tab_id].apps.length; } /** @@ -312,31 +384,22 @@ class FoundBugs { * @return {Object} counts for different types of issues */ getAppsCountByIssues(tab_id, tab_url) { - const apps = this.getApps(tab_id, false, tab_url); - let compatibility = 0; - let insecure = 0; - let latency = 0; - let total = 0; - let all = 0; - - if (apps) { - apps.forEach((app) => { - if (app.hasCompatibilityIssue || app.hasInsecureIssue || app.hasLatencyIssue) { - total++; - } - if (app.hasCompatibilityIssue) { - compatibility++; - } - if (app.hasInsecureIssue) { - insecure++; - } - if (app.hasLatencyIssue) { - latency++; - } - all++; - }); + if (!this._ensure(tab_id)) { + return { + compatibility: 0, + insecure: 0, + latency: 0, + total: 0, + all: 0 + }; } + if (tab_url) { this._checkForCompatibilityIssues(tab_id, tab_url); } + + const { compatibility, insecure, latency } = this._foundApps[tab_id].issueCounts; + const total = compatibility + insecure + latency; + const all = this._foundApps[tab_id].apps.length; + return { compatibility, insecure, @@ -353,20 +416,16 @@ class FoundBugs { * @return {Object} counts for blocked and allowed trackers */ getAppsCountByBlocked(tab_id) { - const apps = this.getApps(tab_id); - let blocked = 0; - let allowed = 0; - - if (apps) { - apps.forEach((app) => { - if (app.blocked) { - blocked++; - } else { - allowed++; - } - }); + if (!this._ensure(tab_id)) { + return { + blocked: 0, + allowed: 0 + }; } + const { blocked } = this._foundApps[tab_id].issueCounts; + const allowed = this._foundApps[tab_id].apps.length - blocked; + return { blocked, allowed @@ -404,15 +463,27 @@ class FoundBugs { } this._foundBugs[tab_id][bug_id].hasLatencyIssue = true; - return bugDb.db.bugs[bug_id].aid; + const { aid } = bugDb.db.bugs[bug_id]; + + const { apps, appsById, issueCounts } = this._foundApps[tab_id]; + if (appsById.hasOwnProperty(aid)) { + const app = apps[appsById[aid]]; + if (!app.hasLatencyIssue) { + issueCounts.latency++; + } + app.hasLatencyIssue = true; + } + + return aid; } /** - * Clear this._foundBugs for a tab_id + * Clear this._foundBugs and this._foundApps for a tab_id * @param {number} tab_id tab id */ clear(tab_id) { delete this._foundBugs[tab_id]; + delete this._foundApps[tab_id]; } } diff --git a/src/classes/PanelData.js b/src/classes/PanelData.js index 32ba5ebca..263acaa5e 100644 --- a/src/classes/PanelData.js +++ b/src/classes/PanelData.js @@ -1,13 +1,13 @@ /** * Panel Data Class * - * Handles data passed to the panel, and manages state by fetching - * from Conf via Dispatcher events. + * Coordinates the assembly and transmission + * of conf, bug, Cliqz module, and reward data to the extension panel * * Ghostery Browser Extension * https://www.ghostery.com/ * - * Copyright 2018 Ghostery, Inc. All rights reserved. + * Copyright 2019 Ghostery, Inc. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -15,6 +15,7 @@ */ import _ from 'underscore'; +import throttle from 'lodash.throttle'; import button from './BrowserButton'; import conf from './Conf'; import foundBugs from './FoundBugs'; @@ -25,6 +26,7 @@ import tabInfo from './TabInfo'; import rewards from './Rewards'; import account from './Account'; import dispatcher from './Dispatcher'; +import { getCliqzAdblockingData, getCliqzAntitrackingData } from '../utils/cliqzModuleData'; import { getActiveTab, flushChromeMemoryCache, processUrl } from '../utils/utils'; import { objectEntries, log } from '../utils/common'; @@ -32,62 +34,353 @@ const SYNC_SET = new Set(globals.SYNC_ARRAY); const IS_EDGE = (globals.BROWSER_INFO.name === 'edge'); const { IS_CLIQZ } = globals; const policy = new Policy(); + /** - * Class for handling data consumed by Ghostery panel. + * PanelData coordinates the assembly and transmission of data to the extension panel + * + * Table of contents + * [PORT MANAGEMENT] functions that handle initializing, storing, and tearing down port connections + * [DATA TRANSFER] functions that send data to the extension panel through existing ports + * [DATA SETTING] functions that set and store non-port data values * @memberOf BackgroundClasses - * @todo make it a Singelton */ class PanelData { - /** - * Create private instance varialbes for use behind public setters - * @return {Object} - */ constructor() { - this._confData = new Map(); - this._trackerData = new Map(); - this._panelView = {}; - this._summaryView = {}; - this._blockingView = {}; - this._rewardsView = {}; - this._settingsView = {}; + this._activeTab = null; + this._categories = []; + this._trackerList = []; + this._uiPorts = new Map(); } + // [PORT MANAGEMENT] /** - * Initialize / update _confData after Conf has loaded. Called via - * initializeGhosteryModules() and Dispatcher event `conf.changed.settings`, which - * looks for changed to Globals.globals.SYNC_ARRAY values. + * Invoked from background.js when Panel, Summary, Blocking, and/or Rewards React components + * initiate a port connection to the background in their componentDidMount lifecycle events + * @param {Object} port the port object passed to the onConnect listener in background.js that calls this function */ - init() { - this._buildConfData(); + initUIPort(port) { + getActiveTab((tab) => { + const { name } = port; + + this._uiPorts.set(name, port); + + if (name === 'panelUIPort') { + const { url } = tab; + + this._activeTab = tab; + this._activeTab.pageHost = url && processUrl(url).host || ''; + + account.getUserSettings().catch(err => log('Failed getting user settings from PanelData#initUIPort:', err)); + } + + this._attachListeners(name, port); + + this._sendInitialData(name, port); + }); } /** - * Get PanelData for a specific view/tab - * @param {string} view panel view name - * @param {Object} tab tab - * @return {Object} view data + * A chunk of the work that initUIPort does, broken out to increase readability + * @param {string} name the name of the port being initialized + * @param {Object} port the port that is being initialized */ - get(view, tab) { - log(`Get data for ${view} view`); - if (view === 'settings') { - return this.settingsView; + _attachListeners(name, port) { + if (name === 'rewardsUIPort') { + port.onDisconnect.addListener(rewards.panelHubClosedListener); + + port.onMessage.addListener((msg) => { + if (msg.name === 'RewardsComponentWillUnmount') { + port.onDisconnect.removeListener(rewards.panelHubClosedListener); + } + }); + } + + if (name === 'panelUIPort') { + port.onDisconnect.addListener(() => { this._activeTab = null; }); } - // update _trackerData with new tab info - this._buildTrackerData(tab); - switch (view) { - case 'panel': - return this.panelView; - case 'summary': - return this.summaryView; - case 'blocking': - return this.blockingView; - case 'rewards': - return this.rewardsView; + + port.onDisconnect.addListener(() => this._uiPorts.delete(name)); + } + + /** + * Another chunk of the work that initUIPort does, broken out to increase readability + * @param {string} name the name of the port being initialized + * @param {Object} port the port that is being initialized + */ + _sendInitialData(name, port) { + if (!this._activeTab) { return; } + + switch (name) { + case 'blockingUIPort': + this._setTrackerListAndCategories(); + port.postMessage(this._getBlockingData()); + break; + case 'panelUIPort': + this._setTrackerListAndCategories(); + port.postMessage(this._getInitData()); + break; + case 'rewardsUIPort': + port.postMessage(this._getRewardsData()); + break; + case 'settingsUIPort': + port.postMessage(this._getSettingsData()); + break; + case 'summaryUIPort': + this._sendCliqzModulesData(port); + this.sendPageLoadTime(this._activeTab.id); + break; default: - return false; + break; + } + } + // [/PORT MANAGEMENT] + + // [DATA TRANSFER] + /** + * An intent-clarifying wrapper used to call sendPageLoadTime in EventHandlers#onBeforeNavigate + * @param {number} tab_id + */ + clearPageLoadTime(tab_id) { + this.sendPageLoadTime(tab_id, true); + } + + /** + * The page_performance content script, injected by EventHandlers#onNavigationCompleted, + * gathers window.performance data and sends it in a message to background.js + * The message handler in background.js calls this function so that the data + * gets forwarded to the extension panel if it's open + * @param {number} tab_id + * @param {boolean} clearData should we simply blank out the page performance data, or fetch it from tabInfo? + */ + sendPageLoadTime(tab_id, clearData) { + if (!this._activeTab || this._activeTab.id !== tab_id) { return; } + + const summaryUIPort = this._uiPorts.get('summaryUIPort'); + if (!summaryUIPort) { return; } + + summaryUIPort.postMessage({ + performanceData: clearData ? false : tabInfo.getTabInfo(tab_id, 'pageTiming') + }); + } + + /** + * Invoked in EventHandlers#onBeforeRequest when a new bug has been found + */ + updatePanelUI = throttle(this._updatePanelUI.bind(this), 600, { leading: true, trailing: true }); // matches donut redraw throttling + _updatePanelUI() { + if (!this._activeTab) { return; } + + if (this._uiPorts.has('summaryUIPort') || this._uiPorts.has('blockingUIPort')) { + this._setTrackerListAndCategories(); + } + + this._uiPorts.forEach((port) => { + const { name } = port; + switch (name) { + case 'blockingUIPort': + port.postMessage(this._getBlockingData()); + break; + case 'panelUIPort': + port.postMessage(this._getPanelUpdateData()); + break; + case 'summaryUIPort': + port.postMessage(this._getSummaryUpdateData()); + this._sendCliqzModulesData(port); + break; + default: + break; + } + }); + } + + /** + * Get conf and tracker data for Blocking view + * @return {Object} Blocking view data + */ + _getBlockingData() { + return { + expand_all_trackers: conf.expand_all_trackers, + site_specific_blocks: conf.site_specific_blocks, + site_specific_unblocks: conf.site_specific_unblocks, + siteNoteScanned: !this._trackerList || false, // TODO make sure this does not change the previous logic + pageUrl: this._activeTab.url, + categories: this._categories, + ...this._getSettingsAndBlockingCommonData() + }; + } + + /** + * Called when and only when the panel is first (re-)opened on a tab + * @return {Object} All data fields used by the panel, summary, and blocking (if in expert mode) views + */ + _getInitData() { + const currentAccount = conf.account; + if (currentAccount && currentAccount.user) { + currentAccount.user.subscriptionsPlus = account.hasScopesUnverified(['subscriptions:plus']); } + const { id } = this._activeTab; + + return { + panel: { + enable_ad_block: conf.enable_ad_block, + enable_anti_tracking: conf.enable_anti_tracking, + enable_smart_block: conf.enable_smart_block, + enable_offers: conf.enable_offers, + is_expanded: conf.is_expanded, + is_expert: conf.is_expert, + is_android: globals.BROWSER_INFO.os === 'android', + language: conf.language, + reload_banner_status: conf.reload_banner_status, + trackers_banner_status: conf.trackers_banner_status, + current_theme: conf.current_theme, + tab_id: id, + unread_offer_ids: rewards.unreadOfferIds, + account: currentAccount, + ...this._getPanelUpdateData(id) + }, + summary: this._getSummaryInitData(), + blocking: conf.is_expert ? this._getBlockingData() : false + }; + } + + // TODO: Determine whether needsReload and/or smartBlock ever actually change! + /** + * Gets panel data that may change when a new tracker is found + * @param {number} tabId + * @return {Object} new needsReload and smartBlock values from tabInfo + */ + _getPanelUpdateData(tabId) { + const id = tabId || this._activeTab.id; + const { needsReload, smartBlock } = tabInfo.getTabInfo(id); + const currentAccount = conf.account; + if (currentAccount && currentAccount.user) { + currentAccount.user.subscriptionsPlus = account.hasScopesUnverified(['subscriptions:plus']); + } + + return { + needsReload: needsReload || { changes: {} }, + smartBlock, + account: currentAccount + }; + } + + // TODO check to see if this might ever need to get updated while panel is open + // if not, does this need to be a port? + /** + * Get rewards data for the Rewards View + * @return {Object} Rewards view data + */ + _getRewardsData() { + return { + enable_offers: conf.enable_offers, + rewards: rewards.storedOffers, + unread_offer_ids: rewards.unreadOfferIds, + }; } + /** + * Get conf and tracker data for Settings View. + * @return {Object} Settings View data + */ + _getSettingsData() { + return { + // custom + categories: this._buildGlobalCategories(), + offer_human_web: !IS_EDGE, + + // properties on conf + alert_bubble_pos: conf.alert_bubble_pos, + alert_bubble_timeout: conf.alert_bubble_timeout, + block_by_default: conf.block_by_default, + bugs_last_updated: conf.bugs_last_updated, + enable_autoupdate: conf.enable_autoupdate, + enable_click2play: conf.enable_click2play, + enable_click2play_social: conf.enable_click2play_social, + enable_human_web: conf.enable_human_web, + enable_offers: conf.enable_offers, + enable_metrics: conf.enable_metrics, + hide_alert_trusted: conf.hide_alert_trusted, + ignore_first_party: conf.ignore_first_party, + notify_library_updates: conf.notify_library_updates, + notify_upgrade_updates: conf.notify_upgrade_updates, + new_app_ids: conf.new_app_ids, + settings_last_imported: conf.settings_last_imported, + settings_last_exported: conf.settings_last_exported, + show_alert: conf.show_alert, + show_badge: conf.show_badge, + show_cmp: conf.show_cmp, + language: conf.language, // required for the setup page that does not have access to panelView data + ...this._getSettingsAndBlockingCommonData() + }; + } + + /** + * Returns the data that would otherwise be missing if the settings view was opened + * without the blocking view having been opened first + * @return {Object} data needed by both blocking and settings views + */ + _getSettingsAndBlockingCommonData() { + return { + selected_app_ids: conf.selected_app_ids, + show_tracker_urls: conf.show_tracker_urls, + toggle_individual_trackers: conf.toggle_individual_trackers + }; + } + + /** + * Get conf and tracker data for Summary View + * @return {Object} Summary view data + */ + _getSummaryInitData() { + const { url, pageHost } = this._activeTab; + + return { + paused_blocking: globals.SESSION.paused_blocking, + paused_blocking_timeout: globals.SESSION.paused_blocking_timeout, + site_blacklist: conf.site_blacklist, + site_whitelist: conf.site_whitelist, + pageHost, + pageUrl: url || '', + siteNotScanned: !this._trackerList || false, + sitePolicy: policy.getSitePolicy(url) || false, + ...this._getSummaryUpdateData() + }; + } + + /** + * Get the summary view data that may change when a new tracker is found + * @return {Object} Fresh alertCounts, categories, and trackerCounts values + */ + _getSummaryUpdateData() { + const { id, url } = this._activeTab; + + return { + alertCounts: foundBugs.getAppsCountByIssues(id, url) || {}, + categories: this._categories, + trackerCounts: foundBugs.getAppsCountByBlocked(id) || {} + }; + } + + /** + * Retrieves antitracking and adblock Cliqz data and sends it to the panel + * @param {Object} port the port to send the data through + */ + _sendCliqzModulesData(port) { + const modules = { adblock: {}, antitracking: {} }; + const { id } = this._activeTab; + + modules.adblock = getCliqzAdblockingData(id); + getCliqzAntitrackingData(id).then((antitrackingData) => { + modules.antitracking = antitrackingData; + port.postMessage(modules); + }).catch(() => { + port.postMessage(modules); + }); + } + // [/DATA TRANSFER] + + + // [DATA SETTING] /** * Update Conf properties with new data from the UI. * Called via setPanelData message. @@ -95,15 +388,12 @@ class PanelData { */ set(data) { let syncSetDataChanged = false; - let otherDataChanged = false; - if (IS_EDGE) { + if (IS_EDGE || IS_CLIQZ) { data.enable_human_web = false; data.enable_offers = false; } if (IS_CLIQZ) { - data.enable_human_web = false; - data.enable_offers = false; data.enable_ad_block = false; data.enable_anti_tracking = false; } @@ -112,11 +402,8 @@ class PanelData { for (const [key, value] of objectEntries(data)) { if (conf.hasOwnProperty(key) && !_.isEqual(conf[key], value)) { conf[key] = value; - if (SYNC_SET.has(key)) { - syncSetDataChanged = true; - } else { - otherDataChanged = true; - } + syncSetDataChanged = SYNC_SET.has(key) ? true : syncSetDataChanged; + // TODO refactor - this work should probably not be the direct responsibility of PanelData } else if (key === 'paused_blocking') { if (typeof value === 'number') { // pause blocking @@ -125,10 +412,7 @@ class PanelData { // enable after timeout setTimeout(() => { globals.SESSION.paused_blocking = false; - // update button - button.update(); - flushChromeMemoryCache(); - dispatcher.trigger('globals.save.paused_blocking'); + this._pausedBlockingHelper(); }, value); } else { // toggle blocking @@ -136,238 +420,154 @@ class PanelData { globals.SESSION.paused_blocking_timeout = 0; } - // update button - button.update(); - flushChromeMemoryCache(); - dispatcher.trigger('globals.save.paused_blocking'); + this._pausedBlockingHelper(); } } - if (data.needsReload) { - getActiveTab((tab) => { - if (tab && tab.id && tabInfo.getTabInfo(tab.id)) { - tabInfo.setTabInfo(tab.id, 'needsReload', data.needsReload); - } - }); + if (data.needsReload && this._activeTab) { + tabInfo.setTabInfo(this._activeTab.id, 'needsReload', data.needsReload); } if (syncSetDataChanged) { // Push conf changes to the server account.saveUserSettings().catch(err => log('PanelData saveUserSettings', err)); } - - if (otherDataChanged) { - // update local _confData. - // if only SYNC_SET data changed _buildConfData will be called through init in dispatch - this._buildConfData(); - } } + // TODO refactor - this work should likely not be the direct responsibility of PanelData /** - * For initial application load, get combined conf and tracker data for - * Panel View, Summary View and, if_expert, Blocking View - * @return {Object} panel data shared by multiple views + * Handles updates that need to happen in response to the extension being paused/unpaused + * Called by #set */ - get panelView() { - const currentAccount = this._confData.get('account'); - if (currentAccount && currentAccount.user) { - currentAccount.user.subscriptionsPlus = account.hasScopesUnverified(['subscriptions:plus']); - } - this._panelView = { - panel: { - enable_ad_block: this._confData.get('enable_ad_block'), - enable_anti_tracking: this._confData.get('enable_anti_tracking'), - enable_smart_block: this._confData.get('enable_smart_block'), - enable_offers: this._confData.get('enable_offers'), - is_expanded: this._confData.get('is_expanded'), - is_expert: this._confData.get('is_expert'), - is_android: globals.BROWSER_INFO.os === 'android', - language: this._confData.get('language'), - reload_banner_status: this._confData.get('reload_banner_status'), - trackers_banner_status: this._confData.get('trackers_banner_status'), - current_theme: this._confData.get('current_theme'), - - needsReload: this._trackerData.get('needsReload'), - smartBlock: this._trackerData.get('smartBlock'), - tab_id: this._trackerData.get('tab_id'), - unread_offer_ids: rewards.unreadOfferIds, - - account: currentAccount - }, - summary: this.summaryView, - blocking: this._confData.get('is_expert') ? this.blockingView : false, - }; - return this._panelView; + _pausedBlockingHelper() { + button.update(); + flushChromeMemoryCache(); + dispatcher.trigger('globals.save.paused_blocking'); } + // TODO analyze whether this and foundBugs#getCategories can be refactored to reduce duplication /** - * Get conf and tracker data for Summary View - * @return {Object} Summary view data + * Build tracker categories based on the current trackerList for a given tab_id. + * @private + * @return {array} array of categories */ - get summaryView() { - this._summaryView = { - paused_blocking: globals.SESSION.paused_blocking, - paused_blocking_timeout: globals.SESSION.paused_blocking_timeout, - site_blacklist: this._confData.get('site_blacklist'), - site_whitelist: this._confData.get('site_whitelist'), - - alertCounts: this._trackerData.get('alertCounts'), - categories: this._trackerData.get('categories'), // duplicated in blockingView, used here just for the donut - pageHost: this._trackerData.get('pageHost'), - pageUrl: this._trackerData.get('pageUrl'), - performanceData: this._trackerData.get('performanceData'), - siteNotScanned: this._trackerData.get('siteNotScanned'), // duplicated in blockingView - sitePolicy: this._trackerData.get('sitePolicy'), - trackerCounts: this._trackerData.get('trackerCounts'), - }; - return this._summaryView; - } + _buildCategories() { + const categories = {}; + const smartBlock = tabInfo.getTabInfo(this._activeTab.id, 'smartBlock'); - /** - * Get conf and tracker data for Blocking View - * @return {Object} Blocking view data - */ - get blockingView() { - this._blockingView = { - expand_all_trackers: this._confData.get('expand_all_trackers'), - selected_app_ids: this._confData.get('selected_app_ids'), - show_tracker_urls: this._confData.get('show_tracker_urls'), - siteNotScanned: this._trackerData.get('siteNotScanned'), - site_specific_blocks: this._confData.get('site_specific_blocks'), - site_specific_unblocks: this._confData.get('site_specific_unblocks'), - toggle_individual_trackers: this._confData.get('toggle_individual_trackers'), - pageUrl: this._trackerData.get('pageUrl'), - categories: this._trackerData.get('categories'), - }; - return this._blockingView; + this._trackerList.forEach((tracker) => { + const trackerState = this._getTrackerState(tracker, smartBlock); + let { cat: category } = tracker; + + if (t(`category_${category}`) === `category_${category}`) { + category = 'uncategorized'; + } + + if (categories.hasOwnProperty(category)) { + categories[category].num_total++; + if (this._addsUpToBlocked(trackerState)) { categories[category].num_blocked++; } + } else { + categories[category] = this._buildCategory(category, trackerState); + } + categories[category].trackers.push(this._buildTracker(tracker, trackerState, smartBlock)); + }); + + const categoryArray = Object.values(categories); + + categoryArray.sort((a, b) => { + a = a.name.toLowerCase(); // eslint-disable-line no-param-reassign + b = b.name.toLowerCase(); // eslint-disable-line no-param-reassign + return (a > b ? 1 : (a < b ? -1 : 0)); + }); + + return categoryArray; } /** - * Get rewards data for the Rewards View - * @return {Object} Rewards view data + * _buildCategories helper + * @param {Object} trackerState object containing various block/allow states of a tracker + * @return {boolean} is the tracker blocked in one of the possible ways? */ - get rewardsView() { - this._rewardsView = { - enable_offers: this._confData.get('enable_offers'), - rewards: rewards.storedOffers, - unread_offer_ids: rewards.unreadOfferIds, - }; - return this._rewardsView; + _addsUpToBlocked({ + ss_blocked, sb_blocked, blocked, ss_allowed, sb_allowed + }) { + return (ss_blocked || sb_blocked || (blocked && !ss_allowed && !sb_allowed)); } /** - * Get conf and tracker data for Settings View. Note we have overlapping properties - * from blockView incase the user is in Simple Mode. - * @return {Object} Settings View data + * _buildCategories helper + * @param {string} category the category of a tracker + * @param {Object} trackerState object containing various block/allow states of a tracker + * @return {Object} an object with data for a new category */ - get settingsView() { - this._settingsView = { - alert_bubble_pos: this._confData.get('alert_bubble_pos'), - alert_bubble_timeout: this._confData.get('alert_bubble_timeout'), - block_by_default: this._confData.get('block_by_default'), - bugs_last_updated: this._confData.get('bugs_last_updated'), - categories: this._confData.get('categories'), - enable_autoupdate: this._confData.get('enable_autoupdate'), - enable_click2play: this._confData.get('enable_click2play'), - enable_click2play_social: this._confData.get('enable_click2play_social'), - enable_human_web: this._confData.get('enable_human_web'), - enable_offers: this._confData.get('enable_offers'), - enable_metrics: this._confData.get('enable_metrics'), - hide_alert_trusted: this._confData.get('hide_alert_trusted'), - ignore_first_party: this._confData.get('ignore_first_party'), - notify_library_updates: this._confData.get('notify_library_updates'), - notify_upgrade_updates: this._confData.get('notify_upgrade_updates'), - offer_human_web: this._confData.get('offer_human_web'), - selected_app_ids: this._confData.get('selected_app_ids'), - new_app_ids: this._confData.get('new_app_ids'), - settings_last_imported: this._confData.get('settings_last_imported'), - settings_last_exported: this._confData.get('settings_last_exported'), - show_alert: this._confData.get('show_alert'), - show_badge: this._confData.get('show_badge'), - show_cmp: this._confData.get('show_cmp'), - show_tracker_urls: this._confData.get('show_tracker_urls'), - toggle_individual_trackers: this._confData.get('toggle_individual_trackers'), - language: this._confData.get('language'), // required for the setup page that does not have access to panelView data + _buildCategory(category, trackerState) { + return { + id: category, + name: t(`category_${category}`), + description: t(`category_${category}+desc`), + img_name: (category === 'advertising') ? 'adv' : // Because AdBlock blocks images with 'advertising' in the name. + (category === 'social_media') ? 'smed' : category, // Because AdBlock blocks images with 'social' in the name. + num_total: 1, + num_blocked: this._addsUpToBlocked(trackerState) ? 1 : 0, + trackers: [] + // expanded: conf.expand_all_trackers }; - - return this._settingsView; } /** - * Build local _confData map. Called during init() and when Conf updates + * _buildCategories helper + * Builds the tracker data object for a given tracker * @private + * @param {Object} tracker + * @param {Object} trackerState + * @param {Object} smartBlock smart blocking stats for the active tab + * @return {Object} object of tracker data */ - _buildConfData() { - this._confData - .set('alert_bubble_pos', conf.alert_bubble_pos) - .set('alert_bubble_timeout', conf.alert_bubble_timeout) - .set('block_by_default', conf.block_by_default) - .set('bugs_last_updated', conf.bugs_last_updated) - .set('categories', this._buildGlobalCategories()) - .set('enable_ad_block', conf.enable_ad_block) - .set('enable_anti_tracking', conf.enable_anti_tracking) - .set('enable_autoupdate', conf.enable_autoupdate) - .set('enable_click2play', conf.enable_click2play) - .set('enable_click2play_social', conf.enable_click2play_social) - .set('enable_human_web', conf.enable_human_web) - .set('enable_metrics', conf.enable_metrics) - .set('enable_offers', conf.enable_offers) - .set('enable_smart_block', conf.enable_smart_block) - .set('hide_alert_trusted', conf.hide_alert_trusted) - .set('ignore_first_party', conf.ignore_first_party) - .set('is_expanded', conf.is_expanded) - .set('is_expert', conf.is_expert) - .set('language', conf.language) - .set('notify_library_updates', conf.notify_library_updates) - .set('notify_upgrade_updates', conf.notify_upgrade_updates) - .set('offer_human_web', !IS_EDGE) - .set('paused_blocking', globals.SESSION.paused_blocking) - .set('reload_banner_status', conf.reload_banner_status) - .set('selected_app_ids', conf.selected_app_ids) - .set('new_app_ids', conf.new_app_ids) - .set('settings_last_imported', conf.settings_last_imported) - .set('settings_last_exported', conf.settings_last_exported) - .set('show_alert', conf.show_alert) - .set('show_badge', conf.show_badge) - .set('show_cmp', conf.show_cmp) - .set('show_tracker_urls', conf.show_tracker_urls) - .set('site_blacklist', conf.site_blacklist) - .set('site_specific_blocks', conf.site_specific_blocks) - .set('site_specific_unblocks', conf.site_specific_unblocks) - .set('site_whitelist', conf.site_whitelist) - .set('toggle_individual_trackers', conf.toggle_individual_trackers) - .set('trackers_banner_status', conf.trackers_banner_status) - .set('expand_all_trackers', conf.expand_all_trackers) - .set('account', conf.account) - .set('current_theme', conf.current_theme); + _buildTracker(tracker, trackerState, smartBlock) { + const { + id, name, cat, sources, hasCompatibilityIssue, hasInsecureIssue, hasLatencyIssue + } = tracker; + const { blocked, ss_allowed, ss_blocked } = trackerState; + + return { + id, + name, + description: '', + blocked, + ss_allowed, + ss_blocked, + shouldShow: true, // used for filtering tracker list + catId: cat, + sources, + warningCompatibility: hasCompatibilityIssue, + warningInsecure: hasInsecureIssue, + warningSlow: hasLatencyIssue, + warningSmartBlock: (smartBlock.blocked.hasOwnProperty(id) && 'blocked') || (smartBlock.unblocked.hasOwnProperty(id) && 'unblocked') || false + }; } /** - * Build local _trackerData map. Called each time a view is fetched. These - * properties represent the initial state of the page on load. They are not updated - * by PanelData.set() - * + * _buildCategories helper + * Computes the various blocked/allowed states for a given tracker * @private - * - * @param {Object} tab active tab + * @param {Object} tracker + * @param {Object} smartBlock + * @return {Object} the tracker's blocked/allowed states */ - _buildTrackerData(tab) { - const tab_id = tab && tab.id; - const tab_url = tab && tab.url || ''; - const pageHost = tab_url && processUrl(tab_url).host || ''; - const trackerList = foundBugs.getApps(tab_id, false, tab_url) || []; - this._trackerData - .set('alertCounts', tab && foundBugs.getAppsCountByIssues(tab_id, tab_url) || {}) - .set('categories', this._buildCategories(tab_id, tab_url, pageHost, trackerList)) - .set('needsReload', tab && tabInfo.getTabInfo(tab_id, 'needsReload') || { changes: {} }) - .set('pageUrl', tab_url || '') - .set('pageHost', pageHost) - .set('performanceData', tab && tabInfo.getTabInfo(tab_id, 'pageTiming')) - .set('sitePolicy', tab && policy.getSitePolicy(tab_url) || false) - .set('siteNotScanned', tab && !foundBugs.getApps(tab_id) || false) - .set('tab_id', tab_id) - .set('trackerCounts', tab && foundBugs.getAppsCountByBlocked(tab_id) || {}) - .set('smartBlock', tabInfo.getTabInfo(tab_id, 'smartBlock')); + _getTrackerState({ id: trackerId }, smartBlock) { + const { pageHost } = this._activeTab; + const selectedAppIds = conf.selected_app_ids; + const pageUnblocks = conf.site_specific_unblocks[pageHost] || []; + const pageBlocks = conf.site_specific_blocks[pageHost] || []; + const smartBlockActive = conf.enable_smart_block; + + return { + blocked: selectedAppIds.hasOwnProperty(trackerId), + ss_allowed: pageUnblocks.includes(+trackerId), + ss_blocked: pageBlocks.includes(+trackerId), + sb_blocked: smartBlockActive && smartBlock.blocked.hasOwnProperty(`${trackerId}`), + sb_allowed: smartBlockActive && smartBlock.unblocked.hasOwnProperty(`${trackerId}`) + }; } /** @@ -392,86 +592,17 @@ class PanelData { } /** - * Build tracker categories based on the current trackerList for a given tab_id. - * - * @private - * - * @param {number} tab_id tab id - * @param {strng} tab_url tab url - * @param {strng} pageHost tab url host - * @param {Object} trackerList list of trackers for the tab - * @return {array} array of categories + * Store the tracker list and categories values to reduce code duplicdation between the blocking and summary data getters, + * and since these values may be accessed 2+ times in a single updatePanelUI call */ - _buildCategories(tab_id, tab_url, pageHost, trackerList) { - const selectedAppIds = this._confData.get('selected_app_ids'); - const pageUnblocks = this._confData.get('site_specific_unblocks')[pageHost] || []; - const pageBlocks = this._confData.get('site_specific_blocks')[pageHost] || []; - const categories = {}; - const categoryArray = []; - const smartBlockActive = this._confData.get('enable_smart_block'); - const smartBlock = tabInfo.getTabInfo(tab_id, 'smartBlock'); - - trackerList.forEach((tracker) => { - let category = tracker.cat; - const blocked = selectedAppIds.hasOwnProperty(tracker.id); - const ss_allowed = pageUnblocks.includes(+tracker.id); - const ss_blocked = pageBlocks.includes(+tracker.id); - const sb_blocked = smartBlockActive && smartBlock.blocked.hasOwnProperty(`${tracker.id}`); - const sb_allowed = smartBlockActive && smartBlock.unblocked.hasOwnProperty(`${tracker.id}`); - - if (t(`category_${category}`) === `category_${category}`) { - category = 'uncategorized'; - } - - if (categories.hasOwnProperty(category)) { - categories[category].num_total++; - if (ss_blocked || sb_blocked || (blocked && !ss_allowed && !sb_allowed)) { - categories[category].num_blocked++; - } - } else { - categories[category] = { - id: category, - name: t(`category_${category}`), - description: t(`category_${category}_desc`), - img_name: (category === 'advertising') ? 'adv' : // Because AdBlock blocks images with 'advertising' in the name. - (category === 'social_media') ? 'smed' : category, // Because AdBlock blocks images with 'social' in the name. - num_total: 1, - num_blocked: (ss_blocked || sb_blocked || (blocked && !ss_allowed && !sb_allowed)) ? 1 : 0, - trackers: [], - expanded: this._confData.get('expand_all_trackers') - }; - } - categories[category].trackers.push({ - id: tracker.id, - name: tracker.name, - description: '', - blocked, - ss_allowed, - ss_blocked, - shouldShow: true, // used for filtering tracker list - catId: category, - sources: tracker.sources, - warningCompatibility: tracker.hasCompatibilityIssue, - warningInsecure: tracker.hasInsecureIssue, - warningSlow: tracker.hasLatencyIssue, - warningSmartBlock: (smartBlock.blocked.hasOwnProperty(tracker.id) && 'blocked') || (smartBlock.unblocked.hasOwnProperty(tracker.id) && 'unblocked') || false, - }); - }); - - let categoryName; - for (categoryName in categories) { - if (categories.hasOwnProperty(categoryName)) { - categoryArray.push(categories[categoryName]); - } - } + _setTrackerListAndCategories() { + const { id, url } = this._activeTab; - categoryArray.sort((a, b) => { - a = a.name.toLowerCase(); // eslint-disable-line no-param-reassign - b = b.name.toLowerCase(); // eslint-disable-line no-param-reassign - return (a > b ? 1 : (a < b ? -1 : 0)); - }); - return categoryArray; + this._trackerList = foundBugs.getApps(id, false, url) || []; + this._categories = this._buildCategories(); } + // [/DATA SETTING] } -export default PanelData; +// return the class as a singleton +export default new PanelData(); diff --git a/src/classes/Rewards.js b/src/classes/Rewards.js index c155d5703..67bd3c050 100644 --- a/src/classes/Rewards.js +++ b/src/classes/Rewards.js @@ -29,7 +29,6 @@ class Rewards { constructor() { this.getStoredOffers(); this.currentOffer = null; - this.panelPort = null; this.ports = new Map(); this.channelsSupported = (typeof chrome.runtime.onConnect === 'object'); this.panelHubClosedListener = this.panelHubClosedListener.bind(this); diff --git a/src/utils/cliqzModuleData.js b/src/utils/cliqzModuleData.js new file mode 100644 index 000000000..e608d31f5 --- /dev/null +++ b/src/utils/cliqzModuleData.js @@ -0,0 +1,58 @@ +/** + * Methods for retrieving antitracking and adblocking data from Cliqz modules + * Used by BrowserButton and PanelData + * + * Ghostery Browser Extension + * https://www.ghostery.com/ + * + * Copyright 2019 Ghostery, Inc. All rights reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0 + */ + +import conf from '../classes/Conf'; +import cliqz from '../classes/Cliqz'; + +const { adblocker, antitracking } = cliqz.modules; + +export function getCliqzAntitrackingData(tabId) { + return new Promise((resolve) => { + if (!conf.enable_anti_tracking || !antitracking.background) { + resolve({ + totalUnsafeCount: 0 + }); + } + + antitracking.background.actions.aggregatedBlockingStats(tabId).then((antitrackingData) => { + let totalUnsafeCount = 0; + for (const category in antitrackingData) { + if (antitrackingData.hasOwnProperty(category)) { + for (const app in antitrackingData[category]) { + if (antitrackingData[category][app] === 'unsafe') { + totalUnsafeCount++; + } + } + } + } + antitrackingData.totalUnsafeCount = totalUnsafeCount; + resolve(antitrackingData); + }).catch(() => { + resolve({ + totalUnsafeCount: 0 + }); + }); + }); +} + +export function getCliqzAdblockingData(tabId) { + if (!conf.enable_ad_block || !adblocker.background) { + return { + totalCount: 0 + }; + } + + const adBlocking = adblocker.background.actions.getAdBlockInfoForTab(tabId); + return adBlocking || { totalCount: 0 }; +} diff --git a/test/src/foundbugs.test.js b/test/src/foundbugs.test.js index 6348275b2..0566528a8 100644 --- a/test/src/foundbugs.test.js +++ b/test/src/foundbugs.test.js @@ -16,7 +16,6 @@ import sinon from 'sinon'; import 'whatwg-fetch'; import bugDb from '../../src/classes/BugDb'; import conf from '../../src/classes/Conf'; -import compDb from '../../src/classes/CompatibilityDb'; import foundBugs from '../../src/classes/FoundBugs'; describe('src/classes/FoundBugs.js', () => { @@ -75,24 +74,6 @@ describe('src/classes/FoundBugs.js', () => { } done(); - - - // const compJson = JSON.stringify({ - // "compatibility": [{"aid":1510,"urls":["members.oreilly.com"]},{"aid":1552,"urls":["travelsmith.com"]},{"aid":2325,"urls":["engadget.com"]}], - // "compatibilityVersion":1466012611577 - // }); - // setFetchStubResponse(200, compJson); - // compDb.init().then(() => { - // const tagsJson = JSON.stringify({ - // "tags":[{"id": 48,"name": "Analytics","description": ""}, - // {"id": 39,"name": "Social","description": ""}], - // "tagsVersion":1412056806620 - // }); - // setFetchStubResponse(200, tagsJson); - // - - // async finished - // }); }); }).catch(err => console.log(err)); }); @@ -112,7 +93,7 @@ describe('src/classes/FoundBugs.js', () => { global.fetch.returns(Promise.resolve(res)); } - describe('testing souce, patter, and app counts', () => { + describe('testing source, pattern, and app counts', () => { test('there should be seven sources', () => { const bugSources = _.flatten(_.pluck(foundBugs.getBugs(url), 'sources')); return expect(bugSources.length).toBe(7); diff --git a/yarn.lock b/yarn.lock index 0b6b33618..732a42976 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17,21 +17,21 @@ "@babel/highlight" "^7.0.0" "@babel/core@^7.2.0": - version "7.2.2" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.2.tgz#07adba6dde27bb5ad8d8672f15fde3e08184a687" - integrity sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b" + integrity sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.2.2" + "@babel/generator" "^7.3.4" "@babel/helpers" "^7.2.0" - "@babel/parser" "^7.2.2" + "@babel/parser" "^7.3.4" "@babel/template" "^7.2.2" - "@babel/traverse" "^7.2.2" - "@babel/types" "^7.2.2" + "@babel/traverse" "^7.3.4" + "@babel/types" "^7.3.4" convert-source-map "^1.1.0" debug "^4.1.0" json5 "^2.1.0" - lodash "^4.17.10" + lodash "^4.17.11" resolve "^1.3.2" semver "^5.4.1" source-map "^0.5.0" @@ -47,14 +47,14 @@ source-map "^0.5.0" trim-right "^1.0.1" -"@babel/generator@^7.2.2": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.2.tgz#fff31a7b2f2f3dad23ef8e01be45b0d5c2fc0132" - integrity sha512-f3QCuPppXxtZOEm5GWPra/uYUjmNQlu9pbAD8D/9jze4pTY83rTtB1igTBSwvkeNlC5gR24zFFkz+2WHLFQhqQ== +"@babel/generator@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.4.tgz#9aa48c1989257877a9d971296e5b73bfe72e446e" + integrity sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg== dependencies: - "@babel/types" "^7.3.2" + "@babel/types" "^7.3.4" jsesc "^2.5.1" - lodash "^4.17.10" + lodash "^4.17.11" source-map "^0.5.0" trim-right "^1.0.1" @@ -66,16 +66,17 @@ "@babel/types" "^7.3.0" esutils "^2.0.0" -"@babel/helper-create-class-features-plugin@^7.3.0": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.2.tgz#ba1685603eb1c9f2f51c9106d5180135c163fe73" - integrity sha512-tdW8+V8ceh2US4GsYdNVNoohq5uVwOf9k6krjwW4E1lINcHgttnWcNqgdoessn12dAy8QkbezlbQh2nXISNY+A== +"@babel/helper-create-class-features-plugin@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.4.tgz#092711a7a3ad8ea34de3e541644c2ce6af1f6f0c" + integrity sha512-uFpzw6L2omjibjxa8VGZsJUPL5wJH0zzGKpoz0ccBkzIa6C8kWNUbiBmQ0rgOKWlHJ6qzmfa6lTiGchiV8SC+g== dependencies: "@babel/helper-function-name" "^7.1.0" "@babel/helper-member-expression-to-functions" "^7.0.0" "@babel/helper-optimise-call-expression" "^7.0.0" "@babel/helper-plugin-utils" "^7.0.0" - "@babel/helper-replace-supers" "^7.2.3" + "@babel/helper-replace-supers" "^7.3.4" + "@babel/helper-split-export-declaration" "^7.0.0" "@babel/helper-function-name@7.0.0-beta.44": version "7.0.0-beta.44" @@ -147,15 +148,15 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz#bbb3fbee98661c569034237cc03967ba99b4f250" integrity sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA== -"@babel/helper-replace-supers@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz#19970020cf22677d62b3a689561dbd9644d8c5e5" - integrity sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA== +"@babel/helper-replace-supers@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.3.4.tgz#a795208e9b911a6eeb08e5891faacf06e7013e13" + integrity sha512-pvObL9WVf2ADs+ePg0jrqlhHoxRXlOa+SHRHzAXIz2xkYuOHfGl+fKxPMaS4Fq+uje8JQPobnertBBvyrWnQ1A== dependencies: "@babel/helper-member-expression-to-functions" "^7.0.0" "@babel/helper-optimise-call-expression" "^7.0.0" - "@babel/traverse" "^7.2.3" - "@babel/types" "^7.0.0" + "@babel/traverse" "^7.3.4" + "@babel/types" "^7.3.4" "@babel/helper-simple-access@^7.1.0": version "7.1.0" @@ -206,23 +207,23 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.2.2", "@babel/parser@^7.2.3": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.2.tgz#95cdeddfc3992a6ca2a1315191c1679ca32c55cd" - integrity sha512-QzNUC2RO1gadg+fs21fi0Uu0OuGNzRKEmgCxoLNzbCdoprLwjfmZwzUrpUNfJPaVRwBpDY47A17yYEGWyRelnQ== +"@babel/parser@^7.2.2", "@babel/parser@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c" + integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ== "@babel/plugin-proposal-class-properties@^7.2.1": - version "7.3.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.0.tgz#272636bc0fa19a0bc46e601ec78136a173ea36cd" - integrity sha512-wNHxLkEKTQ2ay0tnsam2z7fGZUi+05ziDJflEt3AZTP3oXLKHJp9HqhfroB/vdMvt3sda9fAbq7FsG8QPDrZBg== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.4.tgz#410f5173b3dc45939f9ab30ca26684d72901405e" + integrity sha512-lUf8D3HLs4yYlAo8zjuneLvfxN7qfKv1Yzbj5vjqaqMJxgJA3Ipwp4VUJ+OrOdz53Wbww6ahwB8UhB2HQyLotA== dependencies: - "@babel/helper-create-class-features-plugin" "^7.3.0" + "@babel/helper-create-class-features-plugin" "^7.3.4" "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-proposal-object-rest-spread@^7.2.0": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.2.tgz#6d1859882d4d778578e41f82cc5d7bf3d5daf6c1" - integrity sha512-DjeMS+J2+lpANkYLLO+m6GjoTMygYglKmRe6cDTbFv3L9i6mmiE8fe6B8MtCSLZpVXscD5kn7s6SgtHrDoBWoA== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.4.tgz#47f73cf7f2a721aad5c0261205405c642e424654" + integrity sha512-j7VQmbbkA+qrzNqbKHrBsW3ddFnOeva6wzSe/zB7T+xaxGc+RCpwo44wCmRixAIGRoIpmVgvzFzNJqQcO3/9RA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-object-rest-spread" "^7.2.0" @@ -307,9 +308,9 @@ source-map-support "^0.5.9" "@babel/runtime@^7.0.0-rc.2", "@babel/runtime@^7.1.2": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a" - integrity sha512-7jGW8ppV0ant637pIqAcFfQDDH1orEPGJb8aXfUozuCU3QqX7rX4DA8iwrbPrR1hcH0FTTHz47yQnk+bl5xHQA== + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" + integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g== dependencies: regenerator-runtime "^0.12.0" @@ -348,20 +349,20 @@ invariant "^2.2.0" lodash "^4.2.0" -"@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3": - version "7.2.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8" - integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw== +"@babel/traverse@^7.1.5", "@babel/traverse@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06" + integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/generator" "^7.2.2" + "@babel/generator" "^7.3.4" "@babel/helper-function-name" "^7.1.0" "@babel/helper-split-export-declaration" "^7.0.0" - "@babel/parser" "^7.2.3" - "@babel/types" "^7.2.2" + "@babel/parser" "^7.3.4" + "@babel/types" "^7.3.4" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.10" + lodash "^4.17.11" "@babel/types@7.0.0-beta.44": version "7.0.0-beta.44" @@ -372,13 +373,13 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.2": - version "7.3.2" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.2.tgz#424f5be4be633fff33fb83ab8d67e4a8290f5a2f" - integrity sha512-3Y6H8xlUlpbGR+XvawiH0UXehqydTmNmEpozWcXymqwcrwYAl5KMvKtQ+TF6f6E08V6Jur7v/ykdDSF+WDEIXQ== +"@babel/types@^7.0.0", "@babel/types@^7.2.2", "@babel/types@^7.3.0", "@babel/types@^7.3.4": + version "7.3.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed" + integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ== dependencies: esutils "^2.0.2" - lodash "^4.17.10" + lodash "^4.17.11" to-fast-properties "^2.0.0" "@cliqz-oss/dexie@^2.0.4": @@ -386,6 +387,14 @@ resolved "https://registry.yarnpkg.com/@cliqz-oss/dexie/-/dexie-2.0.4.tgz#0e710504e2b9198baa9b046abd3a82731b94d56e" integrity sha512-HxMbBQfdy0CehThTFierXbRPI+PHDEucUUriCCzViAKbCWWQIlL6uZcyDaaPRMPWy45v78lezPB4457kfjS72g== +"@cliqz/adblocker@0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@cliqz/adblocker/-/adblocker-0.3.1.tgz#aa726fa8daf70ca3b3ac0731210aa19ffc7d9640" + integrity sha512-/OfPpOGkllpVFNHeKnvyAYob14CgVBPTZAikeT7seHgWf44Llq5P1tluiHjsEJ8VxwKwAdQ4+xczdyY2Ocvk/g== + dependencies: + tldts "^3.0.0" + tslib "^1.9.3" + "@cliqz/adblocker@^0.6.8": version "0.6.9" resolved "https://registry.yarnpkg.com/@cliqz/adblocker/-/adblocker-0.6.9.tgz#ffe2655f56eb7a73c743d12de6f51826aed36c4e" @@ -394,7 +403,7 @@ punycode "^2.1.1" tslib "^1.9.3" -"@sinonjs/commons@^1.0.2": +"@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2": version "1.3.0" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.3.0.tgz#50a2754016b6f30a994ceda6d9a0a8c36adda849" integrity sha512-j4ZwhaHmwsCb4DlDOIWnI5YyKDNMoNThsmwEpfHx6a1EpsGZ9qYLxP++LMlmBRjtGptGHFsGItJ768snllFWpA== @@ -409,20 +418,26 @@ samsam "1.3.0" "@sinonjs/formatio@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.1.0.tgz#6ac9d1eb1821984d84c4996726e45d1646d8cce5" - integrity sha512-ZAR2bPHOl4Xg6eklUGpsdiIJ4+J1SNag1DHHrG/73Uz/nVwXqjgUtRPLoS+aVyieN9cSbc0E4LsU984tWcDyNg== + version "3.1.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-3.1.1.tgz#3b40de6412b6475cfb046f303b9d8ee13bc7e7ae" + integrity sha512-kMqzWDvtplLhIfqlsDSM2i7T37iHPyEa3Y2Mon/DNE84fnOHheRW0jpuJCxiGUcS5DLs+yGtJPyJpN9rdqMjlA== dependencies: - "@sinonjs/samsam" "^2 || ^3" + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^3.1.0" -"@sinonjs/samsam@^2 || ^3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.1.0.tgz#38146f7be732de96f9f599d7247d71e349bf4bdb" - integrity sha512-IXio+GWY+Q8XUjHUOgK7wx8fpvr7IFffgyXb1bnJFfX3001KmHt35Zq4tp7MXZyjJPCLPuadesDYNk41LYtVjw== +"@sinonjs/samsam@^3.1.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-3.2.0.tgz#58c62b5f1f42e46d039d073d0ae2753da676bf0c" + integrity sha512-j5F1rScewLtx6pbTK0UAjA3jJj4RYiSKOix53YWv+Jzy/AZ69qHxUpU8fwVLjyKbEEud9QrLpv6Ggs7WqTimYw== dependencies: "@sinonjs/commons" "^1.0.2" array-from "^2.1.1" - lodash.get "^4.4.2" + lodash "^4.17.11" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== "@tanem/svg-injector@^1.2.1": version "1.2.1" @@ -430,162 +445,165 @@ integrity sha512-mA5Q5ulPoGQ+e08Vts1R6xw2QU0BKEnMH/KcqoYoS7Gk6imvMTpyFPeu1g+NOZObSIoAzA3/kRzY8m96cEBA2A== "@types/node@*": - version "10.12.24" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.24.tgz#b13564af612a22a20b5d95ca40f1bffb3af315cf" - integrity sha512-GWWbvt+z9G5otRBW8rssOFgRY87J9N/qbhqfjMZ+gUuL6zoL+Hm6gP/8qQBG4jjimqdaNLCehcVapZ/Fs2WjCQ== - -"@webassemblyjs/ast@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" - integrity sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA== - dependencies: - "@webassemblyjs/helper-module-context" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/wast-parser" "1.7.11" - -"@webassemblyjs/floating-point-hex-parser@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz#a69f0af6502eb9a3c045555b1a6129d3d3f2e313" - integrity sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg== - -"@webassemblyjs/helper-api-error@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz#c7b6bb8105f84039511a2b39ce494f193818a32a" - integrity sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg== - -"@webassemblyjs/helper-buffer@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz#3122d48dcc6c9456ed982debe16c8f37101df39b" - integrity sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w== - -"@webassemblyjs/helper-code-frame@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz#cf8f106e746662a0da29bdef635fcd3d1248364b" - integrity sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw== - dependencies: - "@webassemblyjs/wast-printer" "1.7.11" - -"@webassemblyjs/helper-fsm@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz#df38882a624080d03f7503f93e3f17ac5ac01181" - integrity sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A== - -"@webassemblyjs/helper-module-context@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz#d874d722e51e62ac202476935d649c802fa0e209" - integrity sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg== - -"@webassemblyjs/helper-wasm-bytecode@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz#dd9a1e817f1c2eb105b4cf1013093cb9f3c9cb06" - integrity sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ== - -"@webassemblyjs/helper-wasm-section@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz#9c9ac41ecf9fbcfffc96f6d2675e2de33811e68a" - integrity sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-buffer" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/wasm-gen" "1.7.11" - -"@webassemblyjs/ieee754@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz#c95839eb63757a31880aaec7b6512d4191ac640b" - integrity sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ== + version "11.9.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.6.tgz#c632bbcc780a1349673a6e2e9b9dfa8c369d3c74" + integrity sha512-4hS2K4gwo9/aXIcoYxCtHpdgd8XUeDmo1siRCAH3RziXB65JlPqUFMtfy9VPj+og7dp3w1TFjGwYga4e0m9GwA== + +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.7.11.tgz#d7267a1ee9c4594fd3f7e37298818ec65687db63" - integrity sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw== - dependencies: - "@xtuc/long" "4.2.1" - -"@webassemblyjs/utf8@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.7.11.tgz#06d7218ea9fdc94a6793aa92208160db3d26ee82" - integrity sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA== - -"@webassemblyjs/wasm-edit@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz#8c74ca474d4f951d01dbae9bd70814ee22a82005" - integrity sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-buffer" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/helper-wasm-section" "1.7.11" - "@webassemblyjs/wasm-gen" "1.7.11" - "@webassemblyjs/wasm-opt" "1.7.11" - "@webassemblyjs/wasm-parser" "1.7.11" - "@webassemblyjs/wast-printer" "1.7.11" - -"@webassemblyjs/wasm-gen@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz#9bbba942f22375686a6fb759afcd7ac9c45da1a8" - integrity sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/ieee754" "1.7.11" - "@webassemblyjs/leb128" "1.7.11" - "@webassemblyjs/utf8" "1.7.11" - -"@webassemblyjs/wasm-opt@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz#b331e8e7cef8f8e2f007d42c3a36a0580a7d6ca7" - integrity sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-buffer" "1.7.11" - "@webassemblyjs/wasm-gen" "1.7.11" - "@webassemblyjs/wasm-parser" "1.7.11" - -"@webassemblyjs/wasm-parser@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz#6e3d20fa6a3519f6b084ef9391ad58211efb0a1a" - integrity sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-api-error" "1.7.11" - "@webassemblyjs/helper-wasm-bytecode" "1.7.11" - "@webassemblyjs/ieee754" "1.7.11" - "@webassemblyjs/leb128" "1.7.11" - "@webassemblyjs/utf8" "1.7.11" - -"@webassemblyjs/wast-parser@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz#25bd117562ca8c002720ff8116ef9072d9ca869c" - integrity sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/floating-point-hex-parser" "1.7.11" - "@webassemblyjs/helper-api-error" "1.7.11" - "@webassemblyjs/helper-code-frame" "1.7.11" - "@webassemblyjs/helper-fsm" "1.7.11" - "@xtuc/long" "4.2.1" - -"@webassemblyjs/wast-printer@1.7.11": - version "1.7.11" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz#c4245b6de242cb50a2cc950174fdbf65c78d7813" - integrity sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/wast-parser" "1.7.11" - "@xtuc/long" "4.2.1" +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== -"@xtuc/long@4.2.1": - version "4.2.1" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.1.tgz#5c85d662f76fa1d34575766c5dcd6615abcd30d8" - integrity sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g== +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== abab@^2.0.0: version "2.0.0" @@ -633,9 +651,9 @@ acorn@^5.5.0, acorn@^5.5.3: integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== acorn@^6.0.1, acorn@^6.0.5: - version "6.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.0.tgz#b0a3be31752c97a0f7013c5f4903b71a05db6818" - integrity sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw== + version "6.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" + integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== ajv-errors@^1.0.0: version "1.0.1" @@ -663,9 +681,9 @@ ajv@^5.0.0, ajv@^5.2.3, ajv@^5.3.0: json-schema-traverse "^0.3.0" ajv@^6.1.0, ajv@^6.5.0, ajv@^6.5.5: - version "6.9.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.1.tgz#a4d3683d74abc5670e75f0b16520f70a20ea8dc1" - integrity sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA== + version "6.9.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.2.tgz#4927adb83e7f48e5a32b45729744c71ec39c9c7b" + integrity sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg== dependencies: fast-deep-equal "^2.0.1" fast-json-stable-stringify "^2.0.0" @@ -803,6 +821,11 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-find-index@^1.0.1, array-find-index@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -914,11 +937,11 @@ async-limiter@~1.0.0: integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== async@^2.1.4, async@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== + version "2.6.2" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" + integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== dependencies: - lodash "^4.17.10" + lodash "^4.17.11" asynckit@^0.4.0: version "0.4.0" @@ -1499,9 +1522,9 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000936" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000936.tgz#19035ce7c0f44c1562328d73bd455a2390206070" - integrity sha512-gOrcU8d+h5AdrO/Mhnj35vttNvAed2taqzrYDfhJE/qVnLxAaGb1doWlRF7iDex+EQPhkwAHc07RBwixnxpFDQ== + version "1.0.30000939" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000939.tgz#8cb54a9868fe040fbf2e2441408c68b7008912e8" + integrity sha512-nB5tLf3hOs+biXl1lhKjHRgNC0J1I7H52h/t1FP7qxARKKwpB0z+P/JewJLYAlxCBP/q7rxJzQzHHrQMl0viKg== capture-exit@^1.2.0: version "1.2.0" @@ -1571,9 +1594,9 @@ cheerio@^1.0.0-rc.2: parse5 "^3.0.1" chokidar@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.0.tgz#5fcb70d0b28ebe0867eb0f09d5f6a08f29a1efa0" - integrity sha512-5t6G2SH8eO6lCvYOoUpaRnF5Qfd//gd7qJAkwRUw9qlGVkiQ13uwQngqbWWaurOsaAm9+kUGbITADxt6H0XFNQ== + version "2.1.2" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.2.tgz#9c23ea40b01638439e0513864d362aeacc5ad058" + integrity sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg== dependencies: anymatch "^2.0.0" async-each "^1.0.1" @@ -1870,9 +1893,9 @@ core-js@^1.0.0: integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= core-js@^2.4.0, core-js@^2.5.7: - version "2.6.4" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.4.tgz#b8897c062c4d769dd30a0ac5c73976c47f92ea0d" - integrity sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A== + version "2.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" + integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" @@ -2040,9 +2063,9 @@ css-selector-tokenizer@^0.7.0: regexpu-core "^1.0.0" css-what@2.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.2.tgz#c0876d9d0480927d7d4920dcd72af3595649554d" - integrity sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" + integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== cssesc@^0.1.0: version "0.1.0" @@ -2101,9 +2124,9 @@ cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": integrity sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A== cssstyle@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.1.1.tgz#18b038a9c44d65f7a8e428a653b9f6fe42faf5fb" - integrity sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog== + version "1.2.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.2.1.tgz#3aceb2759eaf514ac1a21628d723d6043a819495" + integrity sha512-7DYm8qe+gPx/h77QlCyFmX80+fGaE/6A/Ekl0zaszYOubvySO2saYFdQ78P29D0UsULxFKCetDGNaNRUdSF+2A== dependencies: cssom "0.3.x" @@ -2587,12 +2610,12 @@ doctrine@^2.1.0: esutils "^2.0.2" dom-serializer@0, dom-serializer@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - integrity sha1-BzxpdUbOB4DOI75KKOKT5AvDDII= + version "0.1.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" + integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" + domelementtype "^1.3.0" + entities "^1.1.1" dom-walk@^0.1.0: version "0.1.1" @@ -2609,11 +2632,6 @@ domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs= - domexception@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" @@ -2719,12 +2737,11 @@ entities@^1.1.1, entities@~1.1.1: integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== enzyme-adapter-react-16@^1.1.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz#6d49a3a31c3a0fccf527610f31b837e0f307128a" - integrity sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg== + version "1.10.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.10.0.tgz#12e5b6f4be84f9a2ef374acc2555f829f351fc6e" + integrity sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ== dependencies: enzyme-adapter-utils "^1.10.0" - function.prototype.name "^1.1.0" object.assign "^4.1.0" object.values "^1.1.0" prop-types "^15.6.2" @@ -2743,17 +2760,19 @@ enzyme-adapter-utils@^1.10.0: semver "^5.6.0" enzyme@^3.3.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.8.0.tgz#646d2d5d0798cb98fdec39afcee8a53237b47ad5" - integrity sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw== + version "3.9.0" + resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.9.0.tgz#2b491f06ca966eb56b6510068c7894a7e0be3909" + integrity sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg== dependencies: array.prototype.flat "^1.2.1" cheerio "^1.0.0-rc.2" function.prototype.name "^1.1.0" has "^1.0.3" + html-element-map "^1.0.0" is-boolean-object "^1.0.0" is-callable "^1.1.4" is-number-object "^1.0.3" + is-regex "^1.0.4" is-string "^1.0.4" is-subset "^0.1.1" lodash.escape "^4.0.1" @@ -2803,9 +2822,9 @@ es-to-primitive@^1.2.0: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.47" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.47.tgz#d24232e1380daad5449a817be19bde9729024a11" - integrity sha512-/1TItLfj+TTfWoeRcDn/0FbGV6SNo4R+On2GGVucPU/j3BWnXE2Co8h8CTo4Tu34gFJtnmwS9xiScKs4EjZhdw== + version "0.10.48" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.48.tgz#9a0b31eeded39e64453bcedf6f9d50bbbfb43850" + integrity sha512-CdRvPlX/24Mj5L4NVxTs4804sxiS2CjVprgCmrgoDkdmjdY4D+ySHa7K3jJf8R40dFg0tIm3z/dk326LrnuSGw== dependencies: es6-iterator "~2.0.3" es6-symbol "~3.1.1" @@ -2842,9 +2861,9 @@ escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= escodegen@^1.9.1: - version "1.11.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" - integrity sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw== + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== dependencies: esprima "^3.1.3" estraverse "^4.2.0" @@ -3645,9 +3664,9 @@ global@^4.3.2: process "~0.5.1" globals@^11.0.1, globals@^11.1.0: - version "11.10.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.10.0.tgz#1e09776dffda5e01816b3bb4077c8b59c24eaa50" - integrity sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ== + version "11.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.11.0.tgz#dcf93757fa2de5486fbeed7118538adf789e9c2e" + integrity sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw== globals@^9.18.0: version "9.18.0" @@ -3828,9 +3847,9 @@ home-or-tmp@^3.0.0: integrity sha1-V6j+JM8zzdUkhgoVgh3cJchmcfs= homedir-polyfill@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" - integrity sha1-TCu8inWJmP7r9e1oWA921GdotLw= + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== dependencies: parse-passwd "^1.0.0" @@ -3844,6 +3863,13 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== +html-element-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.0.tgz#19a41940225153ecdfead74f8509154ff1cdc18b" + integrity sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw== + dependencies: + array-filter "^1.0.0" + html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -3880,7 +3906,7 @@ html-minifier@^3.5.8: relateurl "0.2.x" uglify-js "3.4.x" -htmlparser2@^3.9.0: +htmlparser2@^3.9.0, htmlparser2@^3.9.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -3892,18 +3918,6 @@ htmlparser2@^3.9.0: inherits "^2.0.1" readable-stream "^3.1.1" -htmlparser2@^3.9.1: - version "3.10.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.0.tgz#5f5e422dcf6119c0d983ed36260ce9ded0bee464" - integrity sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ== - dependencies: - domelementtype "^1.3.0" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.0.6" - http-https@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/http-https/-/http-https-1.0.0.tgz#2f908dd5f1db4068c058cd6e6d4ce392c913389b" @@ -4085,11 +4099,6 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -4858,9 +4867,9 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.7.0, js-yaml@^3.9.1: - version "3.12.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600" - integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA== + version "3.12.2" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.2.tgz#ef1d067c5a9d9cb65bd72f285b5d8105c77f14fc" + integrity sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -5249,12 +5258,17 @@ lodash.tail@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" integrity sha1-0jM6NtnncXyK0vfKyv7HwytERmQ= +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.0.0, lodash@^4.13.1, lodash@^4.15.0, lodash@^4.16.3, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10: +lodash@^4.0.0, lodash@^4.15.0, lodash@^4.16.3, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@~4.17.10: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== @@ -5320,6 +5334,11 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== + map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -5475,17 +5494,17 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@~1.37.0: - version "1.37.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.37.0.tgz#0b6a0ce6fdbe9576e25f1f2d2fde8830dc0ad0d8" - integrity sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg== +mime-db@~1.38.0: + version "1.38.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" + integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== mime-types@^2.1.12, mime-types@~2.1.19: - version "2.1.21" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.21.tgz#28995aa1ecb770742fe6ae7e58f9181c744b3f96" - integrity sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg== + version "2.1.22" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" + integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== dependencies: - mime-db "~1.37.0" + mime-db "~1.38.0" mime@^1.4.1: version "1.6.0" @@ -5728,15 +5747,15 @@ nice-try@^1.0.4: integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== nise@^1.2.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.8.tgz#ce91c31e86cf9b2c4cac49d7fcd7f56779bfd6b0" - integrity sha512-kGASVhuL4tlAV0tvA34yJYZIVihrUt/5bDwpp4tTluigxUr2bBlJeDXmivb6NuEdFkqvdv/Ybb9dm16PSKUhtw== + version "1.4.10" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.4.10.tgz#ae46a09a26436fae91a38a60919356ae6db143b6" + integrity sha512-sa0RRbj53dovjc7wombHmVli9ZihXbXCQ2uH3TNm03DyvOSIQbxg+pbqDKrk2oxMK1rtLGVlKxcB9rrc6X5YjA== dependencies: "@sinonjs/formatio" "^3.1.0" + "@sinonjs/text-encoding" "^0.7.1" just-extend "^4.0.2" lolex "^2.3.2" path-to-regexp "^1.7.0" - text-encoding "^0.6.4" no-case@^2.2.0: version "2.3.2" @@ -5945,9 +5964,9 @@ npm-bundled@^1.0.1: integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== npm-packlist@^1.1.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.3.0.tgz#7f01e8e44408341379ca98cfd756e7b29bd2626c" - integrity sha512-qPBc6CnxEzpOcc4bjoIBJbYdy0D/LFFPUdxvfwor4/w3vxeE0h6TiOVurCEPpQ6trjN77u/ShyfeJGsbAfB3dA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== dependencies: ignore-walk "^3.0.1" npm-bundled "^1.0.1" @@ -5987,9 +6006,9 @@ number-is-nan@^1.0.0: integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= nwsapi@^2.0.7: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.0.tgz#781065940aed90d9bb01ca5d0ce0fcf81c32712f" - integrity sha512-ZG3bLAvdHmhIjaQ/Db1qvBxsGvFMLIRpQszyqbg31VJ53UP++uZX1/gf3Ut96pdwN9AuDwlMqIYLm0UPCdUeHg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.1.1.tgz#08d6d75e69fd791bdea31507ffafe8c843b67e9c" + integrity sha512-T5GaA1J/d34AC8mkrFD2O0DR17kwJ702ZOtJOsS8RpbsQZVOC2/xYFb1i/cw+xdM54JIlMuojjDOYct8GIWtwg== oauth-sign@~0.9.0: version "0.9.0" @@ -6238,9 +6257,9 @@ p-try@^2.0.0: integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== pako@^1.0.6, pako@~1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.8.tgz#6844890aab9c635af868ad5fecc62e8acbba3ea4" - integrity sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA== + version "1.0.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.9.tgz#410f6784fbba83e5e743be90899fa050e9c6f787" + integrity sha512-tPjtE6dq+dOSg8NMkqRmFjUYH9fect1zmYgB0g6ztQMaVNI7N1CEvLZud2bPHhg7PRgfKEeTshSPiqXb1F7A+A== parallel-transform@^1.1.0: version "1.1.0" @@ -6259,9 +6278,9 @@ param-case@2.1.x: no-case "^2.2.0" parse-asn1@^5.0.0: - version "5.1.3" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.3.tgz#1600c6cc0727365d68b97f3aa78939e735a75204" - integrity sha512-VrPoetlz7B/FqjBLD2f5wBVZvsZVLnRUrxVLfRYhGXCODa/NWE4p3Wp+6+aV3ZPL3KM7/OZmxDIwwijD7yuucg== + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== dependencies: asn1.js "^4.0.0" browserify-aes "^1.0.0" @@ -6426,9 +6445,9 @@ pinkie@^2.0.0: integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= pirates@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.0.tgz#850b18781b4ac6ec58a43c9ed9ec5fe6796addbd" - integrity sha512-8t5BsXy1LUIjn3WWOlOuFDuKswhQb/tkak641lvBgmPOBUQHXveORtlMCp6OdPV1dtuTaEahKA8VNz6uLfKBtA== + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== dependencies: node-modules-regexp "^1.0.0" @@ -6808,7 +6827,7 @@ prompts@^0.1.9: kleur "^2.0.1" sisteransi "^0.1.1" -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0: +prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -6817,14 +6836,6 @@ prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0: object-assign "^4.1.1" react-is "^16.8.1" -prop-types@^15.6.1, prop-types@^15.6.2: - version "15.7.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.1.tgz#2fa61e0a699d428b40320127733ee2931f05d9d1" - integrity sha512-f8Lku2z9kERjOCcnDOPm68EBJAO2K00Q5mSgPAUE/gJuBgsYLbVy6owSrtcHj90zt8PvW+z0qaIIgsIhHOa1Qw== - dependencies: - object-assign "^4.1.1" - react-is "^16.8.1" - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -6968,9 +6979,9 @@ randomatic@^3.0.0: math-random "^1.0.1" randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" - integrity sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A== + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" @@ -7003,19 +7014,19 @@ react-dom@16.6.3: scheduler "^0.11.2" react-dom@^16.0.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.1.tgz#ec860f98853d09d39bafd3a6f1e12389d283dbb4" - integrity sha512-N74IZUrPt6UiDjXaO7UbDDFXeUXnVhZzeRLy/6iqqN1ipfjrhR60Bp5NuBK+rv3GMdqdIuwIl22u1SYwf330bg== + version "16.8.3" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.3.tgz#ae236029e66210783ac81999d3015dfc475b9c32" + integrity sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.1" + scheduler "^0.13.3" -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.1.tgz#a80141e246eb894824fb4f2901c0c50ef31d4cdb" - integrity sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA== +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.3: + version "16.8.3" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d" + integrity sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA== react-lifecycles-compat@^3.0.0: version "3.0.4" @@ -7117,14 +7128,14 @@ react-svg@^5.0.16: prop-types "^15.6.2" react-test-renderer@^16.0.0-0, react-test-renderer@^16.2.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.1.tgz#72845ad9269be526126e97853311982f781767be" - integrity sha512-Bd21TN3+YVl6GZwav6O0T6m5UwGfOj+2+xZH5VH93ToD6M5uclN/c+R1DGX49ueG413KZPUx7Kw3sOYz2aJgfg== + version "16.8.3" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.3.tgz#230006af264cc46aeef94392e04747c21839e05e" + integrity sha512-rjJGYebduKNZH0k1bUivVrRLX04JfIQ0FKJLPK10TAb06XWhfi4gTobooF9K/DEFNW98iGac3OSxkfIJUN9Mdg== dependencies: object-assign "^4.1.1" prop-types "^15.6.2" - react-is "^16.8.1" - scheduler "^0.13.1" + react-is "^16.8.3" + scheduler "^0.13.3" react-timer-mixin@^0.13.3: version "0.13.4" @@ -7165,14 +7176,14 @@ react@16.6.3: scheduler "^0.11.2" react@^16.0.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.1.tgz#ae11831f6cb2a05d58603a976afc8a558e852c4a" - integrity sha512-wLw5CFGPdo7p/AgteFz7GblI2JPOos0+biSoxf1FPsGxWQZdN/pj6oToJs1crn61DL3Ln7mN86uZ4j74p31ELQ== + version "16.8.3" + resolved "https://registry.yarnpkg.com/react/-/react-16.8.3.tgz#c6f988a2ce895375de216edcfaedd6b9a76451d9" + integrity sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.1" + scheduler "^0.13.3" read-file@^0.2.0: version "0.2.0" @@ -7252,10 +7263,10 @@ read-pkg@^2.0.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.0.6, readable-stream@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" - integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== +readable-stream@^3.1.1: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.2.0.tgz#de17f229864c120a9f56945756e4f32c4045245d" + integrity sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -7281,9 +7292,9 @@ readdirp@^2.2.1: readable-stream "^2.0.2" realpath-native@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560" - integrity sha512-+S3zTvVt9yTntFrBpm7TQmQ3tzpCrnA1a/y+3cUHAc9ZR6aIjG0WNLR+Rj79QpJktY+VeW/TQtFlQ1bzsehI8g== + version "1.1.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" + integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== dependencies: util.promisify "^1.0.0" @@ -7444,21 +7455,21 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request-promise-core@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" - integrity sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY= +request-promise-core@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.2.tgz#339f6aababcafdb31c799ff158700336301d3346" + integrity sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag== dependencies: - lodash "^4.13.1" + lodash "^4.17.11" request-promise-native@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.5.tgz#5281770f68e0c9719e5163fd3fab482215f4fda5" - integrity sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU= + version "1.0.7" + resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.7.tgz#a49868a624bdea5069f1251d0a836e0d89aa2c59" + integrity sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w== dependencies: - request-promise-core "1.1.1" - stealthy-require "^1.1.0" - tough-cookie ">=2.3.3" + request-promise-core "1.1.2" + stealthy-require "^1.1.1" + tough-cookie "^2.3.3" request@^2.87.0, request@^2.88.0: version "2.88.0" @@ -7735,10 +7746,10 @@ scheduler@^0.11.2: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.1.tgz#1a217df1bfaabaf4f1b92a9127d5d732d85a9591" - integrity sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A== +scheduler@^0.13.3: + version "0.13.3" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.3.tgz#bed3c5850f62ea9c716a4d781f9daeb9b2a58896" + integrity sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -8151,7 +8162,7 @@ stdout-stream@^1.4.0: dependencies: readable-stream "^2.0.1" -stealthy-require@^1.1.0: +stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= @@ -8411,9 +8422,9 @@ tar@^4: yallist "^3.0.2" terser-webpack-plugin@^1.1.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.2.tgz#9bff3a891ad614855a7dde0d707f7db5a927e3d9" - integrity sha512-1DMkTk286BzmfylAvLXwpJrI7dWa5BnFmscV/2dCr8+c56egFcbaeFAl7+sujAjdmpLam21XRdhA4oifLyiWWg== + version "1.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8" + integrity sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA== dependencies: cacache "^11.0.2" find-cache-dir "^2.0.0" @@ -8479,6 +8490,13 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tldts@^3.0.0: + version "3.1.2" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-3.1.2.tgz#7424a7d18309b14d979ba91526e93b442d8ae1bb" + integrity sha512-MbTi4o2oE7N3kqx+KC5hc6FM8jWY654lPXnwSjA7mw+J83DiW/tR8LBKEKqMvCvjwfbaZMEkIudyZ91c7dxEJg== + dependencies: + punycode "^2.1.1" + tldts@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/tldts/-/tldts-4.0.2.tgz#654f3f665caeccb8e19ff047dc3fc8c64cadc43b" @@ -8541,16 +8559,7 @@ tooltipster@^4.2.6: resolved "https://registry.yarnpkg.com/tooltipster/-/tooltipster-4.2.6.tgz#fbf7a3f5b40bd83e81574e28d9667cf82667bc79" integrity sha1-+/ej9bQL2D6BV04o2WZ8+CZnvHk= -tough-cookie@>=2.3.3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" - integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== - dependencies: - ip-regex "^2.1.0" - psl "^1.1.28" - punycode "^2.1.1" - -tough-cookie@^2.3.4: +tough-cookie@^2.3.3, tough-cookie@^2.3.4: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -8969,14 +8978,14 @@ webpack-sources@^1.1.0, webpack-sources@^1.3.0: source-map "~0.6.1" webpack@^4.20.2: - version "4.29.3" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.3.tgz#e0b406a7b4201ed5e4fb4f84fd7359f9a7db4647" - integrity sha512-xPJvFeB+8tUflXFq+OgdpiSnsCD5EANyv56co5q8q8+YtEasn5Sj3kzY44mta+csCIEB0vneSxnuaHkOL2h94A== - dependencies: - "@webassemblyjs/ast" "1.7.11" - "@webassemblyjs/helper-module-context" "1.7.11" - "@webassemblyjs/wasm-edit" "1.7.11" - "@webassemblyjs/wasm-parser" "1.7.11" + version "4.29.6" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.29.6.tgz#66bf0ec8beee4d469f8b598d3988ff9d8d90e955" + integrity sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" acorn "^6.0.5" acorn-dynamic-import "^4.0.0" ajv "^6.1.0"