diff --git a/app/panel/components/BuildingBlocks/DonutGraph.jsx b/app/panel/components/BuildingBlocks/DonutGraph.jsx
index a4af6d4e0..64fdcf148 100644
--- a/app/panel/components/BuildingBlocks/DonutGraph.jsx
+++ b/app/panel/components/BuildingBlocks/DonutGraph.jsx
@@ -11,7 +11,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import throttle from 'lodash.throttle';
+import { throttle } from 'underscore';
import React from 'react';
import ClassNames from 'classnames';
import {
diff --git a/app/panel/components/BuildingBlocks/StatsGraph.jsx b/app/panel/components/BuildingBlocks/StatsGraph.jsx
index f12a4db55..02f12948c 100644
--- a/app/panel/components/BuildingBlocks/StatsGraph.jsx
+++ b/app/panel/components/BuildingBlocks/StatsGraph.jsx
@@ -11,9 +11,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
+import { isEqual } from 'underscore';
import React from 'react';
import * as D3 from 'd3';
-import isEqual from 'lodash.isequal';
/**
* Generates an animated graph displaying locally stored stats
diff --git a/app/templates/click2play.js b/app/templates/click2play.js
deleted file mode 100644
index 29d130477..000000000
--- a/app/templates/click2play.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/**
- * Ghostery Click2Play
- *
- * 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/.
- */
-module.exports = function (t) {
- let n,
- l = ''; Array.prototype.join; return l += '\n\n
\n\t\n\t\n\n\n\t\n\n\n';
-};
diff --git a/package.json b/package.json
index b18105e8e..aa34cdae5 100644
--- a/package.json
+++ b/package.json
@@ -52,8 +52,6 @@
"foundation-sites": "^6.4.4-rc1",
"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",
@@ -71,6 +69,7 @@
"spanan": "^2.0.0",
"ua-parser-js": "^0.7.17",
"underscore": "^1.8.3",
+ "underscore-template-loader": "^1.0.0",
"url-search-params": "^0.10.2",
"whatwg-fetch": "^3.0.0"
},
@@ -96,7 +95,6 @@
"eslint-plugin-react": "^7.6.1",
"fs-extra": "^4.0.3",
"glob": "^7.1.2",
- "html-loader": "^0.5.1",
"jest": "^23.6.0",
"jsdoc": "^3.5.5",
"jsonfile": "^4.0.0",
diff --git a/src/background.js b/src/background.js
index 6948e575e..58265a7f7 100644
--- a/src/background.js
+++ b/src/background.js
@@ -18,7 +18,7 @@
/**
* @namespace Background
*/
-import _ from 'underscore';
+import { debounce, every, size } from 'underscore';
import moment from 'moment/min/moment-with-locales.min';
import cliqz, { prefs } from './classes/Cliqz';
// object class
@@ -1058,11 +1058,11 @@ function onMessageHandler(request, sender, callback) {
*/
function initializeDispatcher() {
dispatcher.on('conf.save.selected_app_ids', (appIds) => {
- const num_selected = _.size(appIds);
+ const num_selected = size(appIds);
const { db } = bugDb;
db.noneSelected = (num_selected === 0);
- // can't simply compare num_selected and _.size(db.apps) since apps get removed sometimes
- db.allSelected = (!!num_selected && _.every(db.apps, (app, app_id) => appIds.hasOwnProperty(app_id)));
+ // can't simply compare num_selected and size(db.apps) since apps get removed sometimes
+ db.allSelected = (!!num_selected && every(db.apps, (app, app_id) => appIds.hasOwnProperty(app_id)));
});
dispatcher.on('conf.save.site_whitelist', () => {
// TODO debounce with below
@@ -1118,7 +1118,7 @@ function initializeDispatcher() {
}
});
- dispatcher.on('conf.changed.settings', _.debounce((key) => {
+ dispatcher.on('conf.changed.settings', debounce((key) => {
log('Conf value changed for a watched user setting:', key);
}, 200));
diff --git a/src/classes/Account.js b/src/classes/Account.js
index 6b6e23c81..30a569f49 100644
--- a/src/classes/Account.js
+++ b/src/classes/Account.js
@@ -13,7 +13,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import _ from 'underscore';
+import { isEqual } from 'underscore';
import normalize from 'json-api-normalizer';
import build from 'redux-object';
import RSVP from 'rsvp';
@@ -542,7 +542,7 @@ class Account {
}
SYNC_SET.forEach((key) => {
if (settings[key] !== undefined &&
- !_.isEqual(conf[key], settings[key])) {
+ !isEqual(conf[key], settings[key])) {
conf[key] = settings[key];
}
});
diff --git a/src/classes/BugDb.js b/src/classes/BugDb.js
index 1b80aefbf..b2990ed14 100644
--- a/src/classes/BugDb.js
+++ b/src/classes/BugDb.js
@@ -14,7 +14,7 @@
/* eslint no-param-reassign: 0 */
/* eslint no-shadow: 0 */
-import _ from 'underscore';
+import { difference, each, every, keys, reduce, size } from 'underscore';
import conf from './Conf';
import Updatable from './Updatable';
import { defineLazyProperty, flushChromeMemoryCache } from '../utils/utils';
@@ -38,9 +38,9 @@ class BugDb extends Updatable {
updateNewAppIds(new_apps, old_apps) {
log('updating newAppIds...');
- const new_app_ids = _.difference(
- _.keys(new_apps),
- _.keys(old_apps)
+ const new_app_ids = difference(
+ keys(new_apps),
+ keys(old_apps)
).map(Number);
conf.new_app_ids = new_app_ids;
@@ -55,7 +55,7 @@ class BugDb extends Updatable {
if (conf.block_by_default) {
log('applying block-by-default...');
const { selected_app_ids } = conf;
- _.each(new_app_ids, (app_id) => {
+ each(new_app_ids, (app_id) => {
selected_app_ids[app_id] = 1;
});
conf.selected_app_ids = selected_app_ids;
@@ -184,13 +184,13 @@ class BugDb extends Updatable {
log('setting bugdb noneSelected/allSelected...');
- const num_selected = _.size(conf.selected_app_ids);
+ const num_selected = size(conf.selected_app_ids);
db.noneSelected = (num_selected === 0);
// since allSelected is slow to eval, make it lazy
defineLazyProperty(db, 'allSelected', () => {
- const num_selected = _.size(conf.selected_app_ids);
- return (!!num_selected && _.every(db.apps, (app, app_id) => conf.selected_app_ids.hasOwnProperty(app_id)));
+ const num_selected = size(conf.selected_app_ids);
+ return (!!num_selected && every(db.apps, (app, app_id) => conf.selected_app_ids.hasOwnProperty(app_id)));
});
log('processed bugdb...');
@@ -211,7 +211,7 @@ class BugDb extends Updatable {
// pre-trie/legacy db
} else if (old_bugs.hasOwnProperty('bugsVersion') && bugs.version !== old_bugs.bugsVersion) {
- const old_apps = _.reduce(old_bugs.bugs, (memo, bug) => {
+ const old_apps = reduce(old_bugs.bugs, (memo, bug) => {
memo[bug.aid] = true;
return memo;
}, {});
diff --git a/src/classes/Click2PlayDb.js b/src/classes/Click2PlayDb.js
index 635cfbce0..40324e497 100644
--- a/src/classes/Click2PlayDb.js
+++ b/src/classes/Click2PlayDb.js
@@ -66,18 +66,35 @@ class Click2PlayDb extends Updatable {
// TODO memory leak when you close tabs before reset() can run?
reset(tab_id) {
- delete this.allowOnceList[tab_id];
+ if (!this.allowOnceList.hasOwnProperty(tab_id)) { return; }
+
+ const entries = Object.entries(this.allowOnceList[tab_id]);
+ let keep = false;
+ for (const [appID, count] of entries) {
+ const newCount = count - 1;
+ this.allowOnceList[tab_id][appID] = newCount;
+ if (newCount > 0) {
+ keep = true;
+ }
+ }
+ if (!keep) {
+ delete this.allowOnceList[tab_id];
+ }
}
allowedOnce(tab_id, aid) {
- return this.allowOnceList.hasOwnProperty(tab_id) && this.allowOnceList[tab_id].hasOwnProperty(aid);
+ return (
+ this.allowOnceList.hasOwnProperty(tab_id) &&
+ this.allowOnceList[tab_id].hasOwnProperty(aid) &&
+ this.allowOnceList[tab_id][aid] > 0
+ );
}
allowOnce(app_ids, tab_id) {
this.allowOnceList[tab_id] = {};
app_ids.forEach((app_id) => {
- this.allowOnceList[tab_id][app_id] = 1;
+ this.allowOnceList[tab_id][app_id] = 2;
});
}
diff --git a/src/classes/EventHandlers.js b/src/classes/EventHandlers.js
index 24776e50a..dbbc3ef39 100644
--- a/src/classes/EventHandlers.js
+++ b/src/classes/EventHandlers.js
@@ -14,7 +14,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import _ from 'underscore';
+import { map, object, reduce, throttle } from 'underscore';
import bugDb from './BugDb';
import button from './BrowserButton';
import c2pDb from './Click2PlayDb';
@@ -35,7 +35,6 @@ import { isBug } from '../utils/matcher';
import * as utils from '../utils/utils';
const IS_EDGE = (globals.BROWSER_INFO.name === 'edge');
-const RequestsMap = new Map();
/**
* This class is a collection of handlers for
* webNavigation, webRequest and tabs events.
@@ -52,7 +51,7 @@ class EventHandlers {
// Use a 1sec interval to limit calls on pages with a large number of requests.
// Don't use tabId with button.update for cases where tab is switched before throttle delay is reached.
// ToDo: Remove this function when there is an event for AdBlocker:foundAd.
- this._throttleButtonUpdate = _.throttle((tabId) => {
+ this._throttleButtonUpdate = throttle((tabId) => {
button.update(tabId);
}, 1000, { leading: false });
}
@@ -66,11 +65,10 @@ class EventHandlers {
onBeforeNavigate(details) {
const { tabId, frameId, url } = details;
- // frameId === 0 indicates the navigation event ocurred in the content window, not a subframe
+ // frameId === 0 indicates the navigation event occurred in the content window, not a subframe
if (frameId === 0) {
log(`❤ ❤ ❤ Tab ${tabId} navigating to ${url} ❤ ❤ ❤`);
- RequestsMap.clear();
this._clearTabData(tabId);
this._resetNotifications();
// TODO understand why this does not work when placed in the 'reload' branch in onCommitted
@@ -80,6 +78,7 @@ class EventHandlers {
tabInfo.create(tabId, url);
foundBugs.update(tabId);
button.update(tabId);
+ this._eventReset(details.tabId);
// Workaround for foundBugs/tabInfo memory leak when the user triggers
// prefetching/prerendering but never loads the page. Wait two minutes
@@ -105,7 +104,7 @@ class EventHandlers {
tabId, frameId, transitionType, transitionQualifiers
} = details;
- // frameId === 0 indicates the navigation event ocurred in the content window, not a subframe
+ // frameId === 0 indicates the navigation event occurred in the content window, not a subframe
if (frameId === 0) {
// update reload info before creating/clearing tab info
if (transitionType === 'reload' && !transitionQualifiers.includes('forward_back')) {
@@ -239,7 +238,7 @@ class EventHandlers {
if (result) {
utils.sendMessage(
tab_id, 'showUpgradeAlert', {
- translations: _.object(_.map(alert_messages, key => [key, chrome.i18n.getMessage(key)])),
+ translations: object(map(alert_messages, key => [key, chrome.i18n.getMessage(key)])),
language: conf.language,
major_upgrade: globals.JUST_UPGRADED_FROM_7
},
@@ -257,7 +256,7 @@ class EventHandlers {
if (result) {
utils.sendMessage(
tab_id, 'showLibraryUpdateAlert', {
- translations: _.object(_.map(alert_messages, key => [key, chrome.i18n.getMessage(key)])),
+ translations: object(map(alert_messages, key => [key, chrome.i18n.getMessage(key)])),
language: conf.language
},
() => {
@@ -285,8 +284,6 @@ class EventHandlers {
return;
}
- RequestsMap.clear();
-
// Code below executes for top level frame only
log(`foundBugs: ${foundBugs.getAppsCount(details.tabId)}, tab_id: ${details.tabId}`);
@@ -296,11 +293,6 @@ class EventHandlers {
log('onNavigationCompleted injectScript error', err);
});
}
- // The problem is that requests may continue well after onNavigationCompleted
- // This breaks allow once for C2P, as it clears too early
- setTimeout(() => {
- this._eventReset(details.tabId);
- }, 2000);
}
/**
@@ -427,6 +419,9 @@ class EventHandlers {
};
}
+ const smartBlocked = !block ? this.policySmartBlock.shouldBlock(app_id, cat_id, tab_id, page_url, details.type, details.timeStamp) : false;
+ const smartUnblocked = block ? this.policySmartBlock.shouldUnblock(app_id, cat_id, tab_id, page_url, details.type) : false;
+
// process the tracker asynchronously
// very important to block request processing as little as necessary
setTimeout(() => {
@@ -436,20 +431,16 @@ class EventHandlers {
type: details.type,
url: details.url,
block,
+ smartBlocked,
tab_id,
from_frame: details.parentFrameId !== -1
});
}, 1);
- if (block) {
- if (this.policySmartBlock.shouldUnblock(app_id, cat_id, tab_id, page_url, details.type)) {
- return { cancel: false };
- }
+ if ((block && !smartUnblocked) || smartBlocked) {
return this._blockHelper(details, tab_id, app_id, bug_id, request_id, fromRedirect);
}
- if (this.policySmartBlock.shouldBlock(app_id, cat_id, tab_id, page_url, details.type, details.timeStamp)) {
- return this._blockHelper(details, tab_id, app_id, bug_id, request_id);
- }
+
return { cancel: false };
}
@@ -600,13 +591,14 @@ class EventHandlers {
*/
_processBug(details) {
const {
- bug_id, app_id, type, url, block, tab_id
+ bug_id, app_id, type, url, block, smartBlocked, tab_id
} = details;
const tab = tabInfo.getTabInfo(tab_id);
+ const allowedOnce = c2pDb.allowedOnce(details.tab_id, app_id);
let num_apps_old;
- log((block ? 'Blocked' : 'Found'), type, url);
+ log((block || smartBlocked ? 'Blocked' : 'Found'), type, url);
log(`^^^ Pattern ID ${bug_id} on tab ID ${tab_id}`);
if (conf.show_alert) {
@@ -620,13 +612,13 @@ class EventHandlers {
// throttled in PanelData
panelData.updatePanelUI();
- if (block && (conf.enable_click2play || conf.enable_click2playSocial)) {
+ if ((block || smartBlocked) && (conf.enable_click2play || conf.enable_click2playSocial) && !allowedOnce) {
buildC2P(details, app_id);
}
// Note: tab.purplebox handles a race condition where this function is sometimes called before onNavigation()
if (conf.show_alert && tab && !tab.prefetched && tab.purplebox) {
- if (foundBugs.getAppsCount(details.tab_id) > num_apps_old || c2pDb.allowedOnce(details.tab_id, app_id)) {
+ if (foundBugs.getAppsCount(details.tab_id) > num_apps_old || allowedOnce) {
this.purplebox.updateBox(details.tab_id, app_id);
}
}
@@ -678,7 +670,7 @@ class EventHandlers {
const surrogates = surrogatedb.getForTracker(details.url, appId, bugId, ti.host);
if (surrogates.length > 0) {
- code = _.reduce(surrogates, (memo, s) => {
+ code = reduce(surrogates, (memo, s) => {
memo += s.code; // eslint-disable-line no-param-reassign
return memo;
}, '');
diff --git a/src/classes/Latency.js b/src/classes/Latency.js
index b4b1a32b7..f5b331111 100644
--- a/src/classes/Latency.js
+++ b/src/classes/Latency.js
@@ -13,7 +13,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import _ from 'underscore';
+import { isEmpty } from 'underscore';
import foundBugs from './FoundBugs';
/**
* Class for handling request latency data.
@@ -38,7 +38,7 @@ class Latency {
}
// If the latencies object for this request id is empty then this is
// not a tracker. Safe to delete object and return.
- if (_.isEmpty(this.latencies[request_id])) {
+ if (isEmpty(this.latencies[request_id])) {
delete this.latencies[request_id];
return 0;
}
@@ -55,7 +55,7 @@ class Latency {
} = this.latencies[request_id][details.url];
delete this.latencies[request_id][details.url];
- if (_.isEmpty(this.latencies[request_id])) {
+ if (isEmpty(this.latencies[request_id])) {
delete this.latencies[request_id];
}
diff --git a/src/classes/PanelData.js b/src/classes/PanelData.js
index f9d9d1be0..750e4bfc7 100644
--- a/src/classes/PanelData.js
+++ b/src/classes/PanelData.js
@@ -14,8 +14,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import _ from 'underscore';
-import throttle from 'lodash.throttle';
+import { isEqual, throttle } from 'underscore';
import button from './BrowserButton';
import cliqz from './Cliqz';
import conf from './Conf';
@@ -617,7 +616,7 @@ class PanelData {
// Set the conf from data
// TODO can this now be replaced by Object.entries?
for (const [key, value] of objectEntries(data)) {
- if (conf.hasOwnProperty(key) && !_.isEqual(conf[key], value)) {
+ if (conf.hasOwnProperty(key) && !isEqual(conf[key], value)) {
conf[key] = value;
syncSetDataChanged = SYNC_SET.has(key) ? true : syncSetDataChanged;
// TODO refactor - this work should probably be the direct responsibility of Globals
diff --git a/src/classes/Policy.js b/src/classes/Policy.js
index 0989243bc..e6a76243f 100644
--- a/src/classes/Policy.js
+++ b/src/classes/Policy.js
@@ -27,6 +27,7 @@ import globals from './Globals';
*/
export const BLOCK_REASON_BLOCK_PAUSED = 'BLOCK_REASON_BLOCK_PAUSED';
export const BLOCK_REASON_GLOBAL_BLOCKED = 'BLOCK_REASON_GLOBAL_BLOCKED';
+export const BLOCK_REASON_GLOBAL_UNBLOCKED = 'BLOCK_REASON_GLOBAL_UNBLOCKED';
export const BLOCK_REASON_WHITELISTED = 'BLOCK_REASON_WHITELISTED';
export const BLOCK_REASON_BLACKLISTED = 'BLOCK_REASON_BLACKLISTED';
export const BLOCK_REASON_SS_UNBLOCKED = 'BLOCK_REASON_SS_UNBLOCKED';
@@ -124,10 +125,10 @@ class Policy {
return { block: false, reason: BLOCK_REASON_BLOCK_PAUSED };
}
+ const allowedOnce = c2pDb.allowedOnce(tab_id, app_id);
if (conf.selected_app_ids.hasOwnProperty(app_id)) {
if (conf.toggle_individual_trackers && conf.site_specific_unblocks.hasOwnProperty(tab_host) && conf.site_specific_unblocks[tab_host].includes(+app_id)) {
if (this.blacklisted(tab_url)) {
- const allowedOnce = c2pDb.allowedOnce(tab_id, app_id);
return { block: !allowedOnce, reason: allowedOnce ? BLOCK_REASON_C2P_ALLOWED_ONCE : BLOCK_REASON_BLACKLISTED };
}
return { block: false, reason: BLOCK_REASON_SS_UNBLOCKED };
@@ -135,22 +136,19 @@ class Policy {
if (this.whitelisted(tab_url)) {
return { block: false, reason: BLOCK_REASON_WHITELISTED };
}
- const allowedOnce = c2pDb.allowedOnce(tab_id, app_id);
return { block: !allowedOnce, reason: allowedOnce ? BLOCK_REASON_C2P_ALLOWED_ONCE : BLOCK_REASON_GLOBAL_BLOCKED };
}
- // We get here when app_id is not selected for blocking
+ // We get here when app_id is not selected for global blocking
if (conf.toggle_individual_trackers && conf.site_specific_blocks.hasOwnProperty(tab_host) && conf.site_specific_blocks[tab_host].includes(+app_id)) {
if (this.whitelisted(tab_url)) {
return { block: false, reason: BLOCK_REASON_WHITELISTED };
}
- const allowedOnce = c2pDb.allowedOnce(tab_id, app_id);
return { block: !allowedOnce, reason: allowedOnce ? BLOCK_REASON_C2P_ALLOWED_ONCE : BLOCK_REASON_SS_BLOCKED };
}
if (this.blacklisted(tab_url)) {
- const allowedOnce = c2pDb.allowedOnce(tab_id, app_id);
return { block: !allowedOnce, reason: allowedOnce ? BLOCK_REASON_C2P_ALLOWED_ONCE : BLOCK_REASON_BLACKLISTED };
}
- return { block: false, reason: BLOCK_REASON_GLOBAL_BLOCKED };
+ return { block: false, reason: allowedOnce ? BLOCK_REASON_C2P_ALLOWED_ONCE : BLOCK_REASON_GLOBAL_UNBLOCKED };
}
}
diff --git a/src/classes/PolicySmartBlock.js b/src/classes/PolicySmartBlock.js
index 988bf05d0..fe39b3bc3 100644
--- a/src/classes/PolicySmartBlock.js
+++ b/src/classes/PolicySmartBlock.js
@@ -18,6 +18,7 @@ import tabInfo from './TabInfo';
import compDb from './CompatibilityDb';
import globals from './Globals';
import Policy from './Policy';
+import c2pDb from './Click2PlayDb';
import { log } from '../utils/common';
/**
* Class for handling Smart Blocking site policy.
@@ -64,7 +65,7 @@ class PolicySmartBlock {
}
if (reason) {
- log('Smart Blocking unblokced appId', appId, 'for reason:', reason);
+ log('Smart Blocking unblocked appId', appId, 'for reason:', reason);
tabInfo.setTabSmartBlockAppInfo(tabId, appId, reason, false);
return true;
}
@@ -86,7 +87,7 @@ class PolicySmartBlock {
let reason;
- // block if it's been more than 5 seconds since page load started
+ // Block all trackers that load after 5 seconds from when page load started
if (this._requestWasSlow(tabId, appId, requestTimestamp)) {
reason = 'slow';
@@ -103,9 +104,6 @@ class PolicySmartBlock {
const result = (reason === 'slow');
if (result) {
- // We don't want record in tabInfo reasons other than 'slow'
- // Smart blocking should not claim that it unblocks trackers which were unblocked
- // for other reasons before shouldBlock was called for them.
log('Smart Blocking blocked appId', appId, 'for reason:', reason);
tabInfo.setTabSmartBlockAppInfo(tabId, appId, 'slow', true);
}
@@ -121,6 +119,7 @@ class PolicySmartBlock {
* 3. Page is neither whitelisted or blacklisted
* 4. Tracker is not site-specific unblocked
* 5. Tracker is not site-specific blocked
+ * 6. Tracker does not have entry in Click2Play
*
* @param {number} tabId tab id
* @param {string | boolean} appId tracker id
@@ -135,7 +134,8 @@ class PolicySmartBlock {
!globals.SESSION.paused_blocking &&
!this.policy.getSitePolicy(tabUrl) &&
((appId && (!conf.site_specific_unblocks.hasOwnProperty(tabHost) || !conf.site_specific_unblocks[tabHost].includes(+appId))) || appId === false) &&
- ((appId && (!conf.site_specific_blocks.hasOwnProperty(tabHost) || !conf.site_specific_blocks[tabHost].includes(+appId))) || appId === false)
+ ((appId && (!conf.site_specific_blocks.hasOwnProperty(tabHost) || !conf.site_specific_blocks[tabHost].includes(+appId))) || appId === false) &&
+ !c2pDb.db.apps.hasOwnProperty(appId)
);
}
@@ -258,7 +258,7 @@ class PolicySmartBlock {
}
/**
- * Check if request loaded after a threshhold time since page load.
+ * Check if request loaded after a threshold time since page load.
* @param {string} tabId tab id
* @param {string} appId tracker id
* @param {number} requestTimestamp timestamp of the request
diff --git a/src/classes/SurrogateDb.js b/src/classes/SurrogateDb.js
index 4410ac02c..f3d60ff69 100644
--- a/src/classes/SurrogateDb.js
+++ b/src/classes/SurrogateDb.js
@@ -11,7 +11,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import _ from 'underscore';
+import { any, filter, isArray, map } from 'underscore';
import conf from './Conf';
import Updatable from './Updatable';
import { log } from '../utils/common';
@@ -59,14 +59,14 @@ class SurrogateDb extends Updatable {
// convert single values to arrays first
['pattern_id', 'app_id', 'sites', 'match'].forEach((prop) => {
- if (s.hasOwnProperty(prop) && !_.isArray(s[prop])) {
+ if (s.hasOwnProperty(prop) && !isArray(s[prop])) {
s[prop] = [s[prop]];
}
});
// initialize regexes
if (s.hasOwnProperty('match')) {
- s.match = _.map(s.match, match => new RegExp(match, ''));
+ s.match = map(s.match, match => new RegExp(match, ''));
}
if (s.hasOwnProperty('pattern_id') || s.hasOwnProperty('app_id')) {
@@ -108,7 +108,7 @@ class SurrogateDb extends Updatable {
candidates = candidates.concat(this.db.pattern_ids[pattern_id]);
}
- return _.filter(candidates, (surrogate) => {
+ return filter(candidates, (surrogate) => {
// note: does not support *.example.com (exact matches only)
if (surrogate.hasOwnProperty('sites')) { // array of site hosts
if (!surrogate.sites.includes(host_name)) {
@@ -117,7 +117,7 @@ class SurrogateDb extends Updatable {
}
if (surrogate.hasOwnProperty('match')) {
- if (!_.any(surrogate.match, match => script_src.match(match))) {
+ if (!any(surrogate.match, match => script_src.match(match))) {
return false;
}
}
diff --git a/src/classes/Updatable.js b/src/classes/Updatable.js
index 5362f7870..fc9f86321 100644
--- a/src/classes/Updatable.js
+++ b/src/classes/Updatable.js
@@ -11,7 +11,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0
*/
-import _ from 'underscore';
+import { bind, isFunction } from 'underscore';
import globals from './Globals';
import conf from './Conf';
import { getJson, fetchLocalJSONResource } from '../utils/utils';
@@ -56,7 +56,7 @@ class Updatable {
callback
};
- if (_.isFunction(version)) {
+ if (isFunction(version)) {
opts.callback = version;
delete opts.version;
}
@@ -166,7 +166,7 @@ class Updatable {
}
// Fetch new bugs list from remote. Bind the callback param from _remoteFetcher() to this anonymous function
- this._remoteFetcher(_.bind(function (result, list) {
+ this._remoteFetcher(bind(function (result, list) {
// if the fetch worked and we have a list returned
if (result && list) {
const data = this.processList(false, list);
diff --git a/src/utils/click2play.js b/src/utils/click2play.js
index 0c087674c..f87862dad 100644
--- a/src/utils/click2play.js
+++ b/src/utils/click2play.js
@@ -13,7 +13,7 @@
/* eslint no-use-before-define: 0 */
-import _ from 'underscore';
+import { reject } from 'underscore';
import bugDb from '../classes/BugDb';
import c2pDb from '../classes/Click2PlayDb';
import conf from '../classes/Conf';
@@ -22,13 +22,17 @@ import Policy from '../classes/Policy';
import tabInfo from '../classes/TabInfo';
import { log } from './common';
import { sendMessage, processUrl, injectScript } from './utils';
-import c2p_tpl from '../../app/templates/click2play';
+import c2p_tpl from '../../app/templates/click2play.html';
import c2p_images from '../../app/data-images/click2play';
const policy = new Policy();
/**
* Builds Click2Play templates for a given tab_id.
+ *
+ * Restricted Sites: Only show the "allow once" option, since blacklisting overrides
+ * site-specific tracker allow settings.
+ *
* @memberOf BackgroundUtils
*
* @param {Object} details request parameters
@@ -44,7 +48,7 @@ export function buildC2P(details, app_id) {
// click-to-play for social buttons might be disabled
if (!conf.enable_click2play_social) {
- c2pApp = _.reject(c2pApp, c2pAppDef => !!c2pAppDef.button);
+ c2pApp = reject(c2pApp, c2pAppDef => !!c2pAppDef.button);
}
if (!c2pApp.length) {
@@ -81,7 +85,7 @@ export function buildC2P(details, app_id) {
}
}
- c2pHtml.push(c2p_tpl(tplData));
+ c2pHtml.push(c2p_tpl({ data: tplData }));
});
if (app_id === 2575) { // Hubspot forms. Adjust selector.
@@ -181,7 +185,7 @@ function _getHubspotFormSelector(url) {
// Hubspot url has a fixed format
// https://forms.hubspot.com/embed/v3/form/532040/95b5de3a-6d4a-4729-bebf-07c41268d773?callback=hs_reqwest_0&hutk=941df50e9277ee76755310cd78647a08
// The following three parameters are privacy-safe:
- // 532040 - parner id
+ // 532040 - partner id
// 95b5de3a-6d4a-4729-bebf-07c41268d773 - form id on the page
// hs_reqwest_0 - function which will be called on the client after the request
//
diff --git a/src/utils/utils.js b/src/utils/utils.js
index 431963149..a863095f9 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -18,7 +18,7 @@
/**
* @namespace BackgroundUtils
*/
-import _ from 'underscore';
+import { debounce } from 'underscore';
import url from 'url';
import tabInfo from '../classes/TabInfo';
import globals from '../classes/Globals';
@@ -114,7 +114,7 @@ export function isValidTopLevelNavigation(details) {
* (can default to 20 if undefined)
* @memberOf BackgroundUtils
*/
-export const flushChromeMemoryCache = _.debounce(() => {
+export const flushChromeMemoryCache = debounce(() => {
chrome.webRequest.handlerBehaviorChanged();
}, 1000 * 35, true);
diff --git a/webpack.config.js b/webpack.config.js
index 969ce808e..8dd123b6c 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -11,7 +11,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
-// depenencies
+// dependencies
const glob = require('glob');
const path = require('path');
const webpack = require('webpack');
@@ -158,7 +158,7 @@ const config = {
{
test: /\.(html)$/,
use: {
- loader: 'html-loader'
+ loader: 'underscore-template-loader'
}
},{
test: /\.(jsx|js)?/,
diff --git a/yarn.lock b/yarn.lock
index f60c7e197..523c08206 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -903,11 +903,6 @@ ast-types-flow@0.0.7, ast-types-flow@^0.0.7:
resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0=
-ast-types@0.9.6:
- version "0.9.6"
- resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9"
- integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=
-
astral-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
@@ -1466,14 +1461,6 @@ callsites@^2.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
-camel-case@3.0.x:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
- integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
- dependencies:
- no-case "^2.2.0"
- upper-case "^1.1.1"
-
camelcase-keys@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
@@ -1655,13 +1642,6 @@ classnames@^2.2.5:
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
-clean-css@4.2.x:
- version "4.2.1"
- resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
- integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
- dependencies:
- source-map "~0.6.0"
-
clean-webpack-plugin@^0.1.16:
version "0.1.19"
resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-0.1.19.tgz#ceda8bb96b00fe168e9b080272960d20fdcadd6d"
@@ -1802,7 +1782,7 @@ commander@2, commander@^2.11.0, commander@^2.19.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a"
integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==
-commander@2.17.x, commander@~2.17.1:
+commander@~2.17.1:
version "2.17.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==
@@ -2838,14 +2818,6 @@ es6-symbol@^3.1.0, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
d "1"
es5-ext "~0.10.14"
-es6-templates@^0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/es6-templates/-/es6-templates-0.2.3.tgz#5cb9ac9fb1ded6eb1239342b81d792bbb4078ee4"
- integrity sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=
- dependencies:
- recast "~0.11.12"
- through "~2.3.6"
-
escape-string-regexp@^1.0.0, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5, escape-string-regexp@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -3027,7 +2999,7 @@ esprima@^2.6.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
-esprima@^3.1.3, esprima@~3.1.0:
+esprima@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=
@@ -3799,11 +3771,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.1"
-he@1.2.x:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
- integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-
history@^4.7.2:
version "4.7.2"
resolved "https://registry.yarnpkg.com/history/-/history-4.7.2.tgz#22b5c7f31633c5b8021c7f4a8a954ac139ee8d5b"
@@ -3877,30 +3844,6 @@ html-entities@^1.1.3:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=
-html-loader@^0.5.1:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/html-loader/-/html-loader-0.5.5.tgz#6356dbeb0c49756d8ebd5ca327f16ff06ab5faea"
- integrity sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==
- dependencies:
- es6-templates "^0.2.3"
- fastparse "^1.1.1"
- html-minifier "^3.5.8"
- loader-utils "^1.1.0"
- object-assign "^4.1.1"
-
-html-minifier@^3.5.8:
- version "3.5.21"
- resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c"
- integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==
- dependencies:
- camel-case "3.0.x"
- clean-css "4.2.x"
- commander "2.17.x"
- he "1.2.x"
- param-case "2.1.x"
- relateurl "0.2.x"
- uglify-js "3.4.x"
-
htmlparser2@^3.9.0, htmlparser2@^3.9.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
@@ -5163,6 +5106,16 @@ loader-utils@1.1.0:
emojis-list "^2.0.0"
json5 "^0.5.0"
+loader-utils@^0.2.11:
+ version "0.2.17"
+ resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
+ integrity sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=
+ dependencies:
+ big.js "^3.1.3"
+ emojis-list "^2.0.0"
+ json5 "^0.5.0"
+ object-assign "^4.0.1"
+
loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0:
version "1.2.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
@@ -5253,11 +5206,6 @@ 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"
@@ -5288,11 +5236,6 @@ loud-rejection@^1.0.0:
currently-unhandled "^0.4.1"
signal-exit "^3.0.0"
-lower-case@^1.1.1:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
- integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
-
lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
@@ -5752,13 +5695,6 @@ nise@^1.2.0:
lolex "^2.3.2"
path-to-regexp "^1.7.0"
-no-case@^2.2.0:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
- integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
- dependencies:
- lower-case "^1.1.1"
-
node-fetch@^1.0.1, node-fetch@^1.7.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
@@ -6265,13 +6201,6 @@ parallel-transform@^1.1.0:
inherits "^2.0.3"
readable-stream "^2.1.5"
-param-case@2.1.x:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
- integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
- dependencies:
- no-case "^2.2.0"
-
parse-asn1@^5.0.0:
version "5.1.4"
resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc"
@@ -6777,11 +6706,6 @@ pretty-format@^23.6.0:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"
-private@~0.1.5:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
- integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
-
process-nextick-args@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
@@ -7293,16 +7217,6 @@ realpath-native@^1.0.0:
dependencies:
util.promisify "^1.0.0"
-recast@~0.11.12:
- version "0.11.23"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3"
- integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=
- dependencies:
- ast-types "0.9.6"
- esprima "~3.1.0"
- private "~0.1.5"
- source-map "~0.5.0"
-
redent@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
@@ -7415,11 +7329,6 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
-relateurl@0.2.x:
- version "0.2.7"
- resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
- integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
-
remarkable@^1.x:
version "1.7.1"
resolved "https://registry.yarnpkg.com/remarkable/-/remarkable-1.7.1.tgz#aaca4972100b66a642a63a1021ca4bac1be3bff6"
@@ -7987,12 +7896,12 @@ source-map@^0.4.2:
dependencies:
amdefine ">=0.0.4"
-source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0:
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
@@ -8468,7 +8377,7 @@ through2@^2.0.0:
readable-stream "~2.3.6"
xtend "~4.0.1"
-through@^2.3.6, through@~2.3.6:
+through@^2.3.6:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@@ -8638,7 +8547,7 @@ ua-parser-js@^0.7.12, ua-parser-js@^0.7.17, ua-parser-js@^0.7.18:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
-uglify-js@3.4.x, uglify-js@^3.1.4:
+uglify-js@^3.1.4:
version "3.4.9"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
integrity sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==
@@ -8658,6 +8567,14 @@ underscore-contrib@~0.3.0:
dependencies:
underscore "1.6.0"
+underscore-template-loader@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/underscore-template-loader/-/underscore-template-loader-1.0.0.tgz#e0853ee4850d22e00897c4638f36036ac08c0d7e"
+ integrity sha512-4tzm3DY8nLD6mKCQmPk51vu5HW8T61fSGpfkUrlrwF0oOPTinL8Zk7W08xHOmCZ2Sh9bZHVMzVgr/DUM+10iUA==
+ dependencies:
+ fastparse "^1.1.1"
+ loader-utils "^0.2.11"
+
underscore.string@~2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b"
@@ -8735,11 +8652,6 @@ upath@^1.1.0:
resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd"
integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==
-upper-case@^1.1.1:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
- integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
-
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"