From aa1b19f321abf363da08dfebb85da617a2e5d255 Mon Sep 17 00:00:00 2001 From: Martin Bidlingmaier Date: Fri, 15 Sep 2017 17:54:28 +0200 Subject: [PATCH 1/4] Initial barely working version This is a first attempt at a port to the web extensions API. It currently only supports a single block set, no global settings and doesn't try to stop you from circumventing any blocks. The background scripts should already support multiple block sets, it's just a matter of implementing it on the settings page. You'll have to build the addon (via `npm install && ./build`), the unpackaged addons is then in `dist/firefox`. All `./build` does is copy everything to `dist/firefox`, and then replace `background.js` and `settings.js` with their `browserify`ed versions. I plan on adding some comments (function signatures mainly), more tests and of course the missing features if people like this. --- .gitignore | 5 +- .../skin => assets}/freedom_logo_200x40.png | Bin .../skin => assets}/freedom_logo_300x75.png | Bin .../lb-accesscode-dialog.ico | Bin .../default => assets}/lb-lockdown-dialog.ico | Bin .../default => assets}/lb-options-dialog.ico | Bin .../default => assets}/lb-stats-dialog.ico | Bin {chrome/skin => assets}/leechblock16.ico | Bin {chrome/skin => assets}/leechblock16.png | Bin {chrome/skin => assets}/leechblock24.png | Bin {chrome/skin => assets}/leechblock32.ico | Bin {chrome/skin => assets}/leechblock32.png | Bin {chrome/skin => assets}/leechblock64.png | Bin {chrome/skin => assets}/leechblock_logo.png | Bin background.js | 145 ++ block/block.css | 41 + block/block.js | 68 + block/en-US.html | 34 + build | 17 + chrome.manifest | 11 - chrome/content/accesscode.js | 30 - chrome/content/accesscode.xul | 44 - chrome/content/blocked+reload.xhtml | 53 - chrome/content/blocked.xhtml | 50 - chrome/content/blocked_no_ad.xhtml | 43 - chrome/content/browser.js | 1121 ------------ chrome/content/browser.xul | 74 - chrome/content/const.js | 3 - chrome/content/core.js | 539 ------ chrome/content/delayed.xhtml | 50 - chrome/content/delayed_no_ad.xhtml | 43 - chrome/content/fonts.css | 47 - chrome/content/fonts/LICENSE | 202 --- .../content/fonts/Open-Sans-Italic-ext.woff2 | Bin 11908 -> 0 bytes chrome/content/fonts/Open-Sans-Italic.woff2 | Bin 14968 -> 0 bytes .../content/fonts/Open-Sans-Light-ext.woff2 | Bin 12412 -> 0 bytes chrome/content/fonts/Open-Sans-Light.woff2 | Bin 16152 -> 0 bytes chrome/content/fonts/Open-Sans-ext.woff2 | Bin 12288 -> 0 bytes chrome/content/fonts/Open-Sans.woff2 | Bin 15572 -> 0 bytes chrome/content/lockdown.js | 112 -- chrome/content/lockdown.xul | 92 - chrome/content/options.js | 823 --------- chrome/content/options.xul | 1497 ----------------- chrome/content/stats.js | 107 -- chrome/content/stats.xul | 102 -- chrome/content/style.css | 41 - chrome/locale/cs-CZ/leechblock.dtd | 160 -- chrome/locale/cs-CZ/leechblock.properties | 1 - chrome/locale/en-US/leechblock.dtd | 160 -- chrome/locale/en-US/leechblock.properties | 1 - chrome/locale/fr-FR/leechblock.dtd | 160 -- chrome/locale/fr-FR/leechblock.properties | 1 - chrome/locale/pt-BR/leechblock.dtd | 160 -- chrome/locale/pt-BR/leechblock.properties | 1 - chrome/locale/zh-CN/leechblock.dtd | 160 -- chrome/locale/zh-CN/leechblock.properties | 1 - chrome/locale/zh-TW/leechblock.dtd | 160 -- chrome/locale/zh-TW/leechblock.properties | 1 - chrome/skin/toolbar.css | 17 - defaults/preferences/leechblock.js | 188 --- install.rdf | 33 - lib/logic.js | 211 +++ lib/match-pattern.js | 35 + lib/state.js | 184 ++ manifest.json | 30 + package-lock.json | 1355 +++++++++++++++ package.json | 13 + settings/en_US.html | 120 ++ settings/settings.css | 77 + settings/settings.js | 296 ++++ test/logic.js | 237 +++ 71 files changed, 2865 insertions(+), 6291 deletions(-) rename {chrome/skin => assets}/freedom_logo_200x40.png (100%) rename {chrome/skin => assets}/freedom_logo_300x75.png (100%) rename {chrome/icons/default => assets}/lb-accesscode-dialog.ico (100%) rename {chrome/icons/default => assets}/lb-lockdown-dialog.ico (100%) rename {chrome/icons/default => assets}/lb-options-dialog.ico (100%) rename {chrome/icons/default => assets}/lb-stats-dialog.ico (100%) rename {chrome/skin => assets}/leechblock16.ico (100%) rename {chrome/skin => assets}/leechblock16.png (100%) rename {chrome/skin => assets}/leechblock24.png (100%) rename {chrome/skin => assets}/leechblock32.ico (100%) rename {chrome/skin => assets}/leechblock32.png (100%) rename {chrome/skin => assets}/leechblock64.png (100%) rename {chrome/skin => assets}/leechblock_logo.png (100%) create mode 100644 background.js create mode 100644 block/block.css create mode 100644 block/block.js create mode 100644 block/en-US.html create mode 100755 build delete mode 100644 chrome.manifest delete mode 100644 chrome/content/accesscode.js delete mode 100644 chrome/content/accesscode.xul delete mode 100644 chrome/content/blocked+reload.xhtml delete mode 100644 chrome/content/blocked.xhtml delete mode 100644 chrome/content/blocked_no_ad.xhtml delete mode 100644 chrome/content/browser.js delete mode 100644 chrome/content/browser.xul delete mode 100644 chrome/content/const.js delete mode 100644 chrome/content/core.js delete mode 100644 chrome/content/delayed.xhtml delete mode 100644 chrome/content/delayed_no_ad.xhtml delete mode 100644 chrome/content/fonts.css delete mode 100644 chrome/content/fonts/LICENSE delete mode 100644 chrome/content/fonts/Open-Sans-Italic-ext.woff2 delete mode 100644 chrome/content/fonts/Open-Sans-Italic.woff2 delete mode 100644 chrome/content/fonts/Open-Sans-Light-ext.woff2 delete mode 100644 chrome/content/fonts/Open-Sans-Light.woff2 delete mode 100644 chrome/content/fonts/Open-Sans-ext.woff2 delete mode 100644 chrome/content/fonts/Open-Sans.woff2 delete mode 100644 chrome/content/lockdown.js delete mode 100644 chrome/content/lockdown.xul delete mode 100644 chrome/content/options.js delete mode 100644 chrome/content/options.xul delete mode 100644 chrome/content/stats.js delete mode 100644 chrome/content/stats.xul delete mode 100644 chrome/content/style.css delete mode 100644 chrome/locale/cs-CZ/leechblock.dtd delete mode 100644 chrome/locale/cs-CZ/leechblock.properties delete mode 100644 chrome/locale/en-US/leechblock.dtd delete mode 100644 chrome/locale/en-US/leechblock.properties delete mode 100644 chrome/locale/fr-FR/leechblock.dtd delete mode 100644 chrome/locale/fr-FR/leechblock.properties delete mode 100644 chrome/locale/pt-BR/leechblock.dtd delete mode 100644 chrome/locale/pt-BR/leechblock.properties delete mode 100644 chrome/locale/zh-CN/leechblock.dtd delete mode 100644 chrome/locale/zh-CN/leechblock.properties delete mode 100644 chrome/locale/zh-TW/leechblock.dtd delete mode 100644 chrome/locale/zh-TW/leechblock.properties delete mode 100644 chrome/skin/toolbar.css delete mode 100644 defaults/preferences/leechblock.js delete mode 100644 install.rdf create mode 100644 lib/logic.js create mode 100644 lib/match-pattern.js create mode 100644 lib/state.js create mode 100644 manifest.json create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 settings/en_US.html create mode 100644 settings/settings.css create mode 100644 settings/settings.js create mode 100644 test/logic.js diff --git a/.gitignore b/.gitignore index b6a4046..de4d1f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -*.bat -*.txt -*.xpi +dist +node_modules diff --git a/chrome/skin/freedom_logo_200x40.png b/assets/freedom_logo_200x40.png similarity index 100% rename from chrome/skin/freedom_logo_200x40.png rename to assets/freedom_logo_200x40.png diff --git a/chrome/skin/freedom_logo_300x75.png b/assets/freedom_logo_300x75.png similarity index 100% rename from chrome/skin/freedom_logo_300x75.png rename to assets/freedom_logo_300x75.png diff --git a/chrome/icons/default/lb-accesscode-dialog.ico b/assets/lb-accesscode-dialog.ico similarity index 100% rename from chrome/icons/default/lb-accesscode-dialog.ico rename to assets/lb-accesscode-dialog.ico diff --git a/chrome/icons/default/lb-lockdown-dialog.ico b/assets/lb-lockdown-dialog.ico similarity index 100% rename from chrome/icons/default/lb-lockdown-dialog.ico rename to assets/lb-lockdown-dialog.ico diff --git a/chrome/icons/default/lb-options-dialog.ico b/assets/lb-options-dialog.ico similarity index 100% rename from chrome/icons/default/lb-options-dialog.ico rename to assets/lb-options-dialog.ico diff --git a/chrome/icons/default/lb-stats-dialog.ico b/assets/lb-stats-dialog.ico similarity index 100% rename from chrome/icons/default/lb-stats-dialog.ico rename to assets/lb-stats-dialog.ico diff --git a/chrome/skin/leechblock16.ico b/assets/leechblock16.ico similarity index 100% rename from chrome/skin/leechblock16.ico rename to assets/leechblock16.ico diff --git a/chrome/skin/leechblock16.png b/assets/leechblock16.png similarity index 100% rename from chrome/skin/leechblock16.png rename to assets/leechblock16.png diff --git a/chrome/skin/leechblock24.png b/assets/leechblock24.png similarity index 100% rename from chrome/skin/leechblock24.png rename to assets/leechblock24.png diff --git a/chrome/skin/leechblock32.ico b/assets/leechblock32.ico similarity index 100% rename from chrome/skin/leechblock32.ico rename to assets/leechblock32.ico diff --git a/chrome/skin/leechblock32.png b/assets/leechblock32.png similarity index 100% rename from chrome/skin/leechblock32.png rename to assets/leechblock32.png diff --git a/chrome/skin/leechblock64.png b/assets/leechblock64.png similarity index 100% rename from chrome/skin/leechblock64.png rename to assets/leechblock64.png diff --git a/chrome/skin/leechblock_logo.png b/assets/leechblock_logo.png similarity index 100% rename from chrome/skin/leechblock_logo.png rename to assets/leechblock_logo.png diff --git a/background.js b/background.js new file mode 100644 index 0000000..2d9182d --- /dev/null +++ b/background.js @@ -0,0 +1,145 @@ +'use strict' + +const { + formatISODateTime, + getState, + setState, + syncState +} = require('./lib/state') +const moment = require('moment') +const { + quotaTick, + currentStatus, + affectsUrl, + nextNonBlockingMoment +} = require('./lib/logic') + +function getActiveUrl() { + return browser.windows.getLastFocused({populate: true, windowTypes: ['normal']}).then(activeWindow => { + if (activeWindow.focused === true) { + const activeTab = activeWindow.tabs.find(tab => tab.active); + if (activeTab != undefined) { + return activeTab.url; + } + } + + return undefined; + }) +} + +function getAllTabs() { + return browser.tabs.query({windowType: 'normal'}) +} + +function getAllUrls() { + return getAllTabs().then(tabs => tabs.map(tab => tab.url).filter(url => url != undefined)) +} +function encodeBlockInfo(now, blockSet, blockedUrl) { + let hash = "#"; + + if (blockedUrl != undefined) { + hash += encodeURIComponent(blockedUrl) + } + + hash += "#"; + + const unblockingMoment = nextNonBlockingMoment(now, blockSet); + if (unblockingMoment != undefined) { + hash += encodeURIComponent(unblockingMoment.toISOString()); + } + + hash += "#" + + if (blockSet.name != undefined) { + hash += encodeURIComponent(blockSet.name) + } + + return hash; +} + +function blockTab(now, settings, data, tab) { + const blockPageUrl = browser.extension.getURL("block/en-US.html") + let hash = "#" + if (tab.url != undefined) { + hash += encodeURIComponent(tab.url) + } + + hash += "#" + const unblockingMoment = nextNonBlockingMoment(now, settings, data) + if (unblockingMoment != undefined) { + hash += formatISODateTime(unblockingMoment) + } + + hash += "#" + if (settings.name.trim() !== "") { + hash += settings.name + } + + browser.tabs.update(tab.id, {url: blockPageUrl + hash}) +} + +function main() { + getState().then(state => { + syncState(state) + + const lastStatuses = {} + + setInterval(() => { + const now = moment() + // quotaTick for all block sets + Promise.all(state.blockSetSettings.map(settings => { + const data = state.blockSetData[settings.id] + return quotaTick(getActiveUrl, getAllUrls, now, settings, data) + })).then(() => { + // then: check if their status changed to "blocking", and block all + // tabs with affected urls + for (const settings of state.blockSetSettings) { + const id = settings.id + const data = state.blockSetData[id] + const newStatus = currentStatus(now, settings, data) + if (newStatus === "blocking" && lastStatuses[id] !== "blocking") { + getAllTabs().then(tabs => { + Promise.all(tabs.map(tab => { + if (tab.url != undefined && affectsUrl(tab.url, settings, data)) { + blockTab(now, settings, data, tab) + } + })) + }) + } + lastStatuses[id] = newStatus + } + }) + }, 1000) + + // save block set data every minute + setInterval( + () => setState({blockSetData: state.blockSetData}), + 60 * 1000 + ) + + // block a tab if its url changed to something blocked + browser.tabs.onUpdated.addListener((tabId, {url}, tab) => { + if (url == undefined) { + return + } + + const now = moment(); + for (const settings of state.blockSetSettings) { + const data = state.blockSetData[settings.id] + if ( + currentStatus(now, settings, data) === "blocking" && + affectsUrl(url, settings, data) + ) { + blockTab(now, settings, data, tab) + } + } + }); + }) +} + +module.exports = {main} + +if (typeof browser !== "undefined") { + // only executed in browser + main() +} diff --git a/block/block.css b/block/block.css new file mode 100644 index 0000000..dab693e --- /dev/null +++ b/block/block.css @@ -0,0 +1,41 @@ +/* LeechBlock CSS style */ + +body { + background-color: #fff; + color: #2e3436; + font: normal 16px "Open Sans", Arial, sans-serif; + text-align: center; +} + +div { + margin-top: 50px; +} + +h1 { + font-size: 24px; + font-weight: 300; +} + +a { + color: #2ca089; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +.freedom { + font-size: 14px; +} + +.support { + color: #777; + font-size: 14px; + font-style: italic; +} + +.support a { + color: #777; + text-decoration: underline; +} diff --git a/block/block.js b/block/block.js new file mode 100644 index 0000000..503274a --- /dev/null +++ b/block/block.js @@ -0,0 +1,68 @@ +'use strict' + +function decodeBlockInfo(hash) { + let [_, blockedUrl, unblockingDate, blockSetName] = hash.split("#"); + + if (blockedUrl !== "") { + blockedUrl = decodeURIComponent(blockedUrl) + } else { + blockedUrl = undefined + } + + if (unblockingDate !== "") { + unblockingDate = new Date(decodeURIComponent(unblockingDate)) + } else { + unblockingDate = undefined + } + + if (blockSetName !== "") { + blockSetName = decodeURIComponent(blockSetName) + } else { + blockSetName = undefined + } + + return { + blockedUrl, + unblockingDate, + blockSetName + } +} + + +const {blockedUrl, unblockingDate, blockSetName} = + decodeBlockInfo(window.location.hash) + +const blockedUrlLink = document.getElementById('blocked-url-link') +if (blockedUrl != undefined) { + blockedUrlLink.appendChild(document.createTextNode(blockedUrl)) + blockedUrlLink.setAttribute("href", blockedUrl) +} else { + blockedUrlLink.style.display = 'none'; +} + +if (unblockingDate != undefined) { + const now = new Date() + if (unblockingDate < now && blockedUrl != undefined) { + window.location.href = blockedUrl; + } + let formatted; + if (unblockingDate.toDateString() === now.toDateString()) { + formatted = unblockingDate.toLocaleTimeString(); + } else { + formatted = unblockingDate.toLocaleString(); + } + + document.getElementById('never-unblocked').style.display = 'none' + document.getElementById('unblock-time'). + appendChild(document.createTextNode(formatted)) +} else { + document.getElementById('sometime-unblocked').style.display = 'none' +} + +const blockSetInfo = document.getElementById('block-set-info') +if (blockSetName != undefined) { + document.getElementById('block-set-name'). + appendChild(document.createTextNode(blockSetName)) +} else { + document.getElementById('block-set-info').style.display = 'none' +} diff --git a/block/en-US.html b/block/en-US.html new file mode 100644 index 0000000..3c5319d --- /dev/null +++ b/block/en-US.html @@ -0,0 +1,34 @@ + + + + Site Blocked + + + + + + + +
+ LeechBlock logo +
+ + +
+

The page you're attempting to access has been blocked by LeechBlock

+
+ + +
+ () +

+ The page will never be unblocked. +

+

+ The page will be unblocked at . +

+
+ + + + diff --git a/build b/build new file mode 100755 index 0000000..99de15a --- /dev/null +++ b/build @@ -0,0 +1,17 @@ +#!/bin/sh + +PATH="$PATH:$(npm bin)" + +mkdir -p dist/firefox + +cp -r assets dist/firefox +cp -r manifest.json dist/firefox/manifest.json + +# TODO: disable -d in release +browserify -d background.js > dist/firefox/background.js + +cp -r settings dist/firefox +# TODO: disable -d in release +browserify -d settings/settings.js > dist/firefox/settings/settings.js + +cp -r block dist/firefox diff --git a/chrome.manifest b/chrome.manifest deleted file mode 100644 index d91dd2d..0000000 --- a/chrome.manifest +++ /dev/null @@ -1,11 +0,0 @@ -content leechblock chrome/content/ -skin leechblock classic/1.0 chrome/skin/ -locale leechblock cs-CZ chrome/locale/cs-CZ/ -locale leechblock en-US chrome/locale/en-US/ -locale leechblock fr-FR chrome/locale/fr-FR/ -locale leechblock pt-BR chrome/locale/pt-BR/ -locale leechblock zh-CN chrome/locale/zh-CN/ -locale leechblock zh-TW chrome/locale/zh-TW/ - -overlay chrome://browser/content/browser.xul chrome://leechblock/content/browser.xul -style chrome://global/content/customizeToolbar.xul chrome://leechblock/skin/toolbar.css diff --git a/chrome/content/accesscode.js b/chrome/content/accesscode.js deleted file mode 100644 index 422f18e..0000000 --- a/chrome/content/accesscode.js +++ /dev/null @@ -1,30 +0,0 @@ -/* 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/. */ - -/* - * This file contains the code for the access code dialog. It was necessary - * to create this dialog (rather than use nsIPromptService.confirmEx) because - * since Firefox 3 the text in the common dialog can be copied and pasted! - */ - -// Handles access code dialog initialization -// -LeechBlock.accesscodeInit = function () { - let code = window.arguments[0]; // input argument (pass by value) - document.getElementById("lb-accesscode-code").value = code; -} - -// Handles access code dialog OK button -// -LeechBlock.accesscodeOK = function () { - let usercode = window.arguments[1]; // output argument (pass by object) - usercode.value = document.getElementById("lb-accesscode-text").value; - return true; -} - -// Handles access code dialog Cancel button -// -LeechBlock.accesscodeCancel = function () { - return true; -} diff --git a/chrome/content/accesscode.xul b/chrome/content/accesscode.xul deleted file mode 100644 index a6c2a0c..0000000 --- a/chrome/content/accesscode.xul +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - -
- &leechblock; logo -
- - -
-

&pageAccessBlocked1;&leechblock;&pageAccessBlocked2;

-
- - -
- - (&blockSet;) -

&pageUnblockTime;&nullUnblockTime;

-
- - -
- Want to block distractions on other devices and browsers? Check out Freedom!
- Schedule and sync your blocking sessions across Mac, Windows, and iOS devices.
-

Freedom logo

-
- - -
&considerSupport1;
&considerSupport2;&considerSupport3;&considerSupport4;
- - - - diff --git a/chrome/content/blocked.xhtml b/chrome/content/blocked.xhtml deleted file mode 100644 index 65e42b1..0000000 --- a/chrome/content/blocked.xhtml +++ /dev/null @@ -1,50 +0,0 @@ - - %xhtmlDTD; - - %leechblockDTD; -]> - - - - - &siteBlocked; - - - - - - - - - - -
- &leechblock; logo -
- - -
-

&pageAccessBlocked1;&leechblock;&pageAccessBlocked2;

-
- - -
- - (&blockSet;) -

&pageUnblockTime;&nullUnblockTime;

-
- - -
- Want to block distractions on other devices and browsers? Check out Freedom!
- Schedule and sync your blocking sessions across Mac, Windows, and iOS devices.
-

Freedom logo

-
- - -
&considerSupport1;
&considerSupport2;&considerSupport3;&considerSupport4;
- - - - diff --git a/chrome/content/blocked_no_ad.xhtml b/chrome/content/blocked_no_ad.xhtml deleted file mode 100644 index 50bf396..0000000 --- a/chrome/content/blocked_no_ad.xhtml +++ /dev/null @@ -1,43 +0,0 @@ - - %xhtmlDTD; - - %leechblockDTD; -]> - - - - - &siteBlocked; - - - - - - - - - - -
- &leechblock; logo -
- - -
-

&pageAccessBlocked1;&leechblock;&pageAccessBlocked2;

-
- - -
- - (&blockSet;) -

&pageUnblockTime;&nullUnblockTime;

-
- - -
&considerSupport1;
&considerSupport2;&considerSupport3;&considerSupport4;
- - - - diff --git a/chrome/content/browser.js b/chrome/content/browser.js deleted file mode 100644 index d601c7f..0000000 --- a/chrome/content/browser.js +++ /dev/null @@ -1,1121 +0,0 @@ -/* 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/. */ - -/* - * This file contains the code for handling browser-based events. - */ - -// Create progress listener for detecting location change -LeechBlock.progressListener = { - QueryInterface: function(aIID) { - if (aIID.equals(Ci.nsIWebProgressListener) || - aIID.equals(Ci.nsISupportsWeakReference) || - aIID.equals(Ci.nsISupports)) { - return this; - } - throw Cr.NS_NOINTERFACE; - }, - - onLocationChange: function(aProgress, aRequest, aURI) { - LeechBlock.onLocationChange(aProgress.DOMWindow); - }, - - onStateChange: function() {}, - onProgressChange: function() {}, - onStatusChange: function() {}, - onSecurityChange: function() {}, - onLinkIconAvailable: function() {} -}; - -// Handles browser loading -// -LeechBlock.onLoad = function (event) { - // Clean preferences - LeechBlock.cleanPreferences(); - - // Add progress listener for this browser instance - gBrowser.addProgressListener(LeechBlock.progressListener); - - // Apply preference for hiding safe-mode menu items - let hsm = LeechBlock.getBoolPref("hsm"); - let helpSafeMode = document.getElementById("helpSafeMode"); - if (helpSafeMode != null) { - helpSafeMode.hidden = hsm; - } - let appmenu_safeMode = document.getElementById("appmenu_safeMode"); - if (appmenu_safeMode != null) { - appmenu_safeMode.hidden = hsm; - } - - // Apply preference for hiding context menu - let hcm = LeechBlock.getBoolPref("hcm"); - let contextMenu = document.getElementById("leechblock-context-menu"); - if (contextMenu != null) { - contextMenu.hidden = hcm; - } - - // Apply preference for hiding "Time Left" toolbar item - LeechBlock.updateTimeLeft(); - - // Get current time in milliseconds - let time = Date.now(); - - // Get current time in seconds - let now = Math.floor(time / 1000); - - for (let set = 1; set <= 6; set++) { - // Reset time data if currently invalid - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 4) { - timedata[5] = 0; // add lockdown end time (null) - } else if (timedata.length != 5) { - timedata = [now, 0, 0, 0, 0]; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - - // Get sites for block set from HTTP source (if specified) - let sitesURL = LeechBlock.getUniCharPref("sitesURL" + set) - .replace(/\$S/, set) - .replace(/\$T/, time); - if (sitesURL != "") { - try { - let req = new XMLHttpRequest(); - req.set = set; - req.open("GET", sitesURL, true); - req.overrideMimeType("text/plain"); - req.onreadystatechange = function () { - LeechBlock.httpRequestCallback(req); - }; - req.send(null); - } catch (e) { - console.warn("[LB] Cannot load sites from URL: " + sitesURL); - } - } - } -} - -// Handles browser unloading -// -LeechBlock.onUnload = function (event) { - // Remove progress listener for this browser instance - gBrowser.removeProgressListener(LeechBlock.progressListener); -} - -// Handles HTTP request callback -// -LeechBlock.httpRequestCallback = function (req) { - if (req.readyState == 4 && req.status == 200) { - // Get sites from response text - let sites = req.responseText; - sites = sites.replace(/\s+/g, " ").replace(/(^ +)|( +$)|(\w+:\/+)/g, ""); - sites = sites.split(" ").sort().join(" "); // sort alphabetically - - // Get regular expressions to match sites - let regexps = LeechBlock.getRegExpSites(sites); - - // Update preferences - LeechBlock.setUniCharPref("sites" + req.set, sites); - LeechBlock.setUniCharPref("blockRE" + req.set, regexps.block); - LeechBlock.setUniCharPref("allowRE" + req.set, regexps.allow); - LeechBlock.setUniCharPref("keywordRE" + req.set, regexps.keyword); - } -} - -// Handles location changing -// -LeechBlock.onLocationChange = function (win) { - //console.log("[LB] win.location: " + win.location); - - // Stop user bypassing extension by loading browser.xul - if (win.location.href.startsWith(LeechBlock.BROWSER_URL)) { - win.location = "about:blank"; - return; - } - - // Get parsed URL for this page - let parsedURL = LeechBlock.getParsedURL(win.location.href); - let pageURL = parsedURL.page; - - // Only check page the first time (i.e. no check when tab re-activated) - if (win.leechblockPageURL != pageURL) { - win.leechblockPageURL = pageURL; - LeechBlock.checkWindow(parsedURL, win, false); - } - - LeechBlock.updateTimeLeft(win.leechblockSecsLeft); -} - -// Handles page loading -// -LeechBlock.onPageLoad = function (event) { - //console.log("[LB] doc.load: " + event.target.location); - - let doc = event.target; - let win = doc.defaultView; - - // Get parsed URL for this page - let parsedURL = LeechBlock.getParsedURL(win.location.href); - let pageURL = parsedURL.page; - - // Quick exit for system pages - if (pageURL == LeechBlock.BROWSER_URL - || pageURL == "about:blank" - || pageURL == "about:newtab") { - return; - } - - // Clear preference for allowed origin/page if this is different origin/page - let ao = LeechBlock.getUniCharPref("ao"); - let ap = LeechBlock.getUniCharPref("ap"); - if (/^http|file|about/.test(parsedURL.protocol) && (win.frameElement == null)) { - if (parsedURL.origin != ao) { - LeechBlock.clearUserPref("ao"); - } - if (parsedURL.page != ap) { - LeechBlock.clearUserPref("ap"); - } - } - - // Hide extension in about:addons (if option selected) - if (pageURL.toLowerCase() == "about:addons" && LeechBlock.getBoolPref("ham")) { - LeechBlock.hideExtension(doc); - } - - // Handle blocking/delaying page - let blockingPage = doc.getElementById("leechblockBlockingPage"); - let delayingPage = doc.getElementById("leechblockDelayingPage"); - if ((blockingPage != null || delayingPage != null) - && parsedURL.args != null && parsedURL.args.length >= 2) { - // Get block set and URL (including hash part) of blocked page - let blockedSet = parsedURL.args.shift(); - let blockedURL = parsedURL.query.substring(3); // retains original separators (& or ;) - if (parsedURL.hash != null) { - blockedURL += "#" + parsedURL.hash; - } - - // Set URL of blocked page - let blockedURLSpan = doc.getElementById("leechblockBlockedURLSpan"); - if (blockedURLSpan != null) { - if (blockedURL.length > 60) { - blockedURLSpan.textContent = blockedURL.substring(0, 57) + "..."; - } else { - blockedURLSpan.textContent = blockedURL; - } - } - - // Set name of block set - let blockedSetSpan = doc.getElementById("leechblockBlockedSetSpan"); - if (blockedSetSpan != null) try { - let blockedSetName = LeechBlock.getUniCharPref("setName" + blockedSet); - if (blockedSetName == "") { - blockedSetSpan.textContent += " " + blockedSet; // "Block Set N" (localized) - } else { - blockedSetSpan.textContent = blockedSetName; // Custom block set name - } - } catch (e) { - // Die gracefully - } - - // Set unblock time - let unblockTimeSpan = doc.getElementById("leechblockUnblockTimeSpan"); - if (unblockTimeSpan != null) { - let unblockTime = LeechBlock.getUnblockTime(blockedSet); - if (unblockTime != null) { - if (unblockTime.getDate() == new Date().getDate()) { - // Same day: show time only - unblockTimeSpan.textContent = unblockTime.toLocaleTimeString(); - } else { - // Different day: show date and time - unblockTimeSpan.textContent = unblockTime.toLocaleString(); - } - } - } - - // Set hyperlink to blocked page - let blockedURLLink = doc.getElementById("leechblockBlockedURLLink"); - if (blockedURLLink != null) { - blockedURLLink.setAttribute("href", blockedURL); - } - - // Start countdown if this is a delaying page - if (delayingPage != null) try { - // Get delay value in seconds - let delaySecs = LeechBlock.getCharPref("delaySecs" + blockedSet); - - // Set countdown seconds on page - let secondsSpan = doc.getElementById("leechblockDelaySecondsSpan"); - if (secondsSpan != null) { - secondsSpan.textContent = delaySecs; - } - - // Start countdown timer - let countdown = { - win: win, - blockedURL: blockedURL, - blockedSet: blockedSet, - delaySecs: delaySecs - }; - doc.leechblockCountdownInterval = setInterval( - LeechBlock.onCountdownTimer, - 1000, countdown); - } catch (e) { - // Die gracefully - } - } - - for (let set = 1; set <= 6; set++) { - // Get regular expressions for matching sites to block/allow - let blockRE = LeechBlock.getUniCharPref("blockRE" + set); - if (blockRE == "") continue; // no block for this set - let allowRE = LeechBlock.getUniCharPref("allowRE" + set); - - // Test URL against block/allow regular expressions - if (LeechBlock.testURL(pageURL, blockRE, allowRE)) { - // Add document to list for block set - LeechBlock.addLoadedDoc(set, doc); - } - } - - // Start clocking time spent on this page - LeechBlock.clockPageTime(doc, true, doc.hasFocus()); - - // Add event listeners for this window - if (pageURL != "about:addons") { - doc.addEventListener("focus", LeechBlock.onPageFocus, false); - doc.addEventListener("blur", LeechBlock.onPageBlur, false); - } - //doc.addEventListener("pagehide", LeechBlock.onPageUnload, false); - win.addEventListener("pagehide", LeechBlock.onWinUnload, false); - - // Check page (in case of keyword matches) - LeechBlock.checkWindow(parsedURL, win, false); -} - -// Checks the URL of a window and applies block if necessary -// -LeechBlock.checkWindow = function (parsedURL, win, isRepeat) { - //console.log("[LB] checkWindow: " + win.location); - - let doc = win.document; - - // Quick exit for non-http/non-file/non-about URLs - if (!/^(http|file|about)/.test(parsedURL.protocol)) { - return; - } - - // Quick exit for embedded pages (according to preference) - if (win.frameElement != null && !LeechBlock.getBoolPref("bep")) { - return; - } - - // Quick exit for allowed origin/page - let ao = LeechBlock.getUniCharPref("ao"); - let ap = LeechBlock.getUniCharPref("ap"); - if (parsedURL.origin == ao || parsedURL.page == ap) { - return; - } - - // Get URL without hash part (unless it's a hash-bang part) - let pageURL = parsedURL.page; - if (parsedURL.hash != null && /^!/.test(parsedURL.hash)) { - pageURL += "#" + parsedURL.hash; - } - - // Get current time/date - let timedate = new Date(); - - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - win.leechblockSecsLeft = Infinity; - - for (let set = 1; set <= 6; set++) { - // Get regular expressions for matching sites to block/allow - let blockRE = LeechBlock.getUniCharPref("blockRE" + set); - if (blockRE == "") continue; // no block for this set - let allowRE = LeechBlock.getUniCharPref("allowRE" + set); - let keywordRE = LeechBlock.getUniCharPref("keywordRE" + set); - - // Get preferences for preventing access to about:addons and about:config - let prevAddons = LeechBlock.getBitPref("prevAddons", set); - let prevConfig = LeechBlock.getBitPref("prevConfig", set); - - // Test URL against block/allow regular expressions - if (LeechBlock.testURL(pageURL, blockRE, allowRE) - || (prevAddons && /^about:addons/i.test(pageURL)) - || (prevConfig && /^about:(config|support)/i.test(pageURL))) { - // Get preferences for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let times = LeechBlock.getCharPref("times" + set); - let minPeriods = LeechBlock.getMinPeriods(times); - let limitMins = LeechBlock.getCharPref("limitMins" + set); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let periodStart = LeechBlock.getTimePeriodStart(now, limitPeriod); - let conjMode = LeechBlock.getBitPref("conjMode", set); - let daySel = LeechBlock.decodeDays(LeechBlock.getIntPref("days" + set)); - let blockURL = LeechBlock.getUniCharPref("blockURL" + set); - let activeBlock = LeechBlock.getBitPref("activeBlock", set); - - // Start timer for repeat check - if (doc.leechblockCheckTimeout == undefined) { - doc.leechblockCheckTimeout = setTimeout( - LeechBlock.repeatCheckWindow, - LeechBlock.getIntPref("repeatCheckPeriod"), win); - } - - // Check day - let onSelectedDay = daySel[timedate.getDay()]; - - // Check time periods - let secsLeftBeforePeriod = Infinity; - if (onSelectedDay && times != "") { - // Get number of minutes elapsed since midnight - let mins = timedate.getHours() * 60 + timedate.getMinutes(); - - // Check each time period in turn - for (let mp of minPeriods) { - if (mins >= mp.start && mins < mp.end) { - secsLeftBeforePeriod = 0; - } else if (mins < mp.start) { - // Compute exact seconds before this time period starts - let secs = (mp.start - mins) * 60 - timedate.getSeconds(); - if (secs < secsLeftBeforePeriod) { - secsLeftBeforePeriod = secs; - } - } - } - } - - // Check time limit - let secsLeftBeforeLimit = Infinity; - if (onSelectedDay && limitMins != "" && limitPeriod != "") { - // Compute exact seconds before this time limit expires - secsLeftBeforeLimit = limitMins * 60; - if (timedata.length == 5 && timedata[2] == periodStart) { - let secs = secsLeftBeforeLimit - timedata[3]; - secsLeftBeforeLimit = Math.max(0, secs); - } - } - - let withinTimePeriods = (secsLeftBeforePeriod == 0); - let afterTimeLimit = (secsLeftBeforeLimit == 0); - - // Check lockdown condition - let lockdown = (timedata.length == 5 && timedata[4] > now); - - // Check for keywords - let keywords = (keywordRE == "") - || LeechBlock.checkKeywords(doc, keywordRE); - - // Determine whether this page should now be blocked - let doBlock = lockdown - || (!conjMode && (withinTimePeriods || afterTimeLimit) && keywords) - || (conjMode && (withinTimePeriods && afterTimeLimit) && keywords); - - // Redirect page if all relevant block conditions are fulfilled - if (doBlock && (!isRepeat || activeBlock)) { - // Get final URL for block page - blockURL = blockURL.replace(/\$S/g, set).replace(/\$U/g, pageURL); - - // Redirect page according to preference - if (LeechBlock.getBoolPref("kpb")) { - win.location = blockURL; - } else { - win.location.replace(blockURL); - } - - return; // nothing more to do - } - - // Update seconds left before block - let secsLeft = conjMode - ? (secsLeftBeforePeriod + secsLeftBeforeLimit) - : Math.min(secsLeftBeforePeriod, secsLeftBeforeLimit); - if (secsLeft < win.leechblockSecsLeft) { - win.leechblockSecsLeft = secsLeft; - win.leechblockSecsLeftSet = set; - } - } - } - - // Determine whether to display warning message - let warnSecs = LeechBlock.getCharPref("warnSecs"); - if (warnSecs != "") { - let set = win.leechblockSecsLeftSet; - if (win.leechblockSecsLeft > warnSecs) { - // Reset flag - LeechBlock.doneWarning[set - 1] = false; - } else if (!LeechBlock.doneWarning[set - 1]) { - // Set flag - LeechBlock.doneWarning[set - 1] = true; - // Display warning message - let setName = LeechBlock.getUniCharPref("setName" + set); - LeechBlock.alertBlockWarning(set, setName, win.leechblockSecsLeft); - } - } -} - -// Checks document for keywords -// -LeechBlock.checkKeywords = function (doc, keywordRE) { - //console.log("[LB] checkKeywords: " + doc.location); - - // Create regular expression (case insensitive) - let regexp = new RegExp(keywordRE, "i"); - - // Get all text nodes in document - let textNodes = doc.evaluate( - "//text()", - doc, - null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, - null); - - //console.log("[LB] Checking " + textNodes.snapshotLength + " text node(s) for keywords..."); - - for (let i = 0; i < textNodes.snapshotLength; i++) { - if (regexp.test(textNodes.snapshotItem(i).data)) { - return true; // keyword found - } - } - - return false; // no keyword found -} - -// Handles callback for repeat check -// -LeechBlock.repeatCheckWindow = function (win) { - //console.log("[LB] repeatCheckWindow: " + win.location); - - try { - let doc = win.document; - - doc.leechblockCheckTimeout = undefined; - - // Get parsed URL for this page - let parsedURL = LeechBlock.getParsedURL(win.location.href); - - // Force update of time spent on this page - if (doc.hasFocus()) { - // Page is open and has focus - LeechBlock.clockPageTime(doc, false, false); - LeechBlock.clockPageTime(doc, true, true); - } else { - // Page is open but does not have focus - LeechBlock.clockPageTime(doc, false, false); - LeechBlock.clockPageTime(doc, true, false); - } - - LeechBlock.checkWindow(parsedURL, win, true); - - // If page is active document, update time left - if (doc == gBrowser.contentDocument) { - LeechBlock.updateTimeLeft(win.leechblockSecsLeft); - } - } catch (e) { - // Die gracefully - } -} - -// Handles page gaining focus -// -LeechBlock.onPageFocus = function (event) { - //console.log("[LB] doc.focus: " + event.target.location); - - let doc = event.target.ownerDocument != null - ? event.target.ownerDocument - : event.target; - - for (let set = 1; set <= 6; set++) { - // Set active document in list for block set - LeechBlock.setActiveLoadedDoc(set, doc); - } - - LeechBlock.clockPageTime(doc, true, true); -} - -// Handles page losing focus -// -LeechBlock.onPageBlur = function (event) { - //console.log("[LB] doc.blur: " + event.target.location); - - let doc = event.target.ownerDocument != null - ? event.target.ownerDocument - : event.target; - - LeechBlock.clockPageTime(doc, true, false); - - if (doc.leechblockCountdownInterval != undefined) { - // Clear countdown timer - clearInterval(doc.leechblockCountdownInterval); - doc.leechblockCountdownInterval = undefined; - - // Strike line through countdown text (if this is delaying page) - let countdownText = doc.getElementById("leechblockCountdownText"); - if (countdownText != null) { - countdownText.style.textDecoration = "line-through"; - } - } -} - -// Handles page unloading -// -LeechBlock.onPageUnload = function (event) { - //console.log("[LB] doc.unload: " + event.target.location); - - let doc = event.target.ownerDocument != null - ? event.target.ownerDocument - : event.target; - - LeechBlock.clockPageTime(doc, false, false); -} - -// Handles window gaining focus -// -LeechBlock.onWinFocus = function (event) { - //console.log("[LB] win.focus: " + event.target.location); - - let win = event.currentTarget; - let doc = win.document; -} - -// Handles window losing focus -// -LeechBlock.onWinBlur = function (event) { - //console.log("[LB] win.blur: " + event.target.location); - - let win = event.currentTarget; - let doc = win.document; -} - -// Handles window unloading -// -LeechBlock.onWinUnload = function (event) { - //console.log("[LB] win.unload: " + event.target.location); - - let win = event.currentTarget; - let doc = win.document; - - LeechBlock.clockPageTime(doc, false, false); - - if (doc.leechblockCheckTimeout != undefined) { - // Clear timer for repeat check - clearTimeout(doc.leechblockCheckTimeout); - doc.leechblockCheckTimeout = undefined; - } - - if (doc.leechblockAddonsTimeout != undefined) { - // Clear timer for hiding extension - clearTimeout(doc.leechblockAddonsTimeout); - doc.leechblockAddonsTimeout = undefined; - } - - if (doc.leechblockCountdownInterval != undefined) { - // Clear countdown timer - clearInterval(doc.leechblockCountdownInterval); - doc.leechblockCountdownInterval = undefined; - } - - for (let set = 1; set <= 6; set++) { - // Remove document from list for block set - LeechBlock.removeLoadedDoc(set, doc); - } -} - -// Clocks time spent on page -// -LeechBlock.clockPageTime = function (doc, open, focus) { - // Get current time in milliseconds - let time = Date.now(); - - // Clock time during which page has been open - let secsOpen = 0; - if (open) { - if (doc.leechblockOpenTime == undefined) { - // Set start time for this page - doc.leechblockOpenTime = time; - } - } else { - if (doc.leechblockOpenTime != undefined) { - if (doc.location != null && /^(http|file)/.test(doc.location.href)) { - // Calculate seconds spent on this page (while open) - secsOpen = Math.round((time - doc.leechblockOpenTime) / 1000); - } - - doc.leechblockOpenTime = undefined; - } - } - - // Clock time during which page has been focused - let secsFocus = 0; - if (focus) { - if (doc.leechblockFocusTime == undefined) { - // Set focus time for this page - doc.leechblockFocusTime = time; - } - } else { - if (doc.leechblockFocusTime != undefined) { - if (doc.location != null && /^(http|file)/.test(doc.location.href)) { - // Calculate seconds spent on this page (while focused) - secsFocus = Math.round((time - doc.leechblockFocusTime) / 1000); - } - - doc.leechblockFocusTime = undefined; - } - } - - // Update time data if necessary - if (secsOpen > 0 || secsFocus > 0) { - LeechBlock.updateTimeData(doc, secsOpen, secsFocus); - } -} - -// Updates data for time spent on page -// -LeechBlock.updateTimeData = function (doc, secsOpen, secsFocus) { - //console.log("[LB] updateTimeData: doc = " + doc.location); - //console.log("[LB] updateTimeData: secsOpen = " + secsOpen); - //console.log("[LB] updateTimeData: secsFocus = " + secsFocus); - - // Get parsed URL for this page - let parsedURL = LeechBlock.getParsedURL(doc.location.href); - let pageURL = parsedURL.page; - - // Get current time/date - let timedate = new Date(); - - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - for (let set = 1; set <= 6; set++) { - // Get regular expressions for matching sites to block/allow - let blockRE = LeechBlock.getUniCharPref("blockRE" + set); - if (blockRE == "") continue; // no block for this set - let allowRE = LeechBlock.getUniCharPref("allowRE" + set); - - // Test URL against block/allow regular expressions - if (LeechBlock.testURL(pageURL, blockRE, allowRE)) { - // Get preferences for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let countFocus = LeechBlock.getBitPref("countFocus", set); - let times = LeechBlock.getCharPref("times" + set); - let minPeriods = LeechBlock.getMinPeriods(times); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let conjMode = LeechBlock.getBitPref("conjMode", set); - let daySel = LeechBlock.decodeDays(LeechBlock.getIntPref("days" + set)); - - // Avoid over-counting time when multiple documents loaded - if (!countFocus && !LeechBlock.isActiveLoadedDoc(set, doc)) continue; - - // Get start of this time period - let periodStart = LeechBlock.getTimePeriodStart(now, limitPeriod); - - // Reset time data if currently invalid - if (timedata.length != 5) { - timedata = [now, 0, 0, 0, 0]; - } - - // Get number of seconds spent on page (focused or open) - let seconds = countFocus ? secsFocus : secsOpen; - - // Update data for total time spent - timedata[1] = +timedata[1] + seconds; - - // Determine whether we should count time spent on page in - // specified time period (we should only count time on selected - // days -- and in conjunction mode, only within time periods) - let countTimeSpentInPeriod = daySel[timedate.getDay()]; - if (countTimeSpentInPeriod && conjMode) { - countTimeSpentInPeriod = false; - - // Get number of minutes elapsed since midnight - let mins = timedate.getHours() * 60 + timedate.getMinutes(); - - // Check each time period in turn - for (let mp of minPeriods) { - if (mins >= mp.start && mins < mp.end) { - countTimeSpentInPeriod = true; - } - } - } - - // Update data for time spent in specified time period - if (countTimeSpentInPeriod && periodStart > 0 && timedata[2] >= 0) { - if (timedata[2] != periodStart) { - // We've entered a new time period, so start new count - timedata[2] = periodStart; - timedata[3] = seconds; - } else { - // We haven't entered a new time period, so keep counting - timedata[3] = +timedata[3] + seconds; - } - //console.log("[LB] Set " + set + ": " + timedata[3] + "s since " + new Date(timedata[2] * 1000).toLocaleString()); - } - - // Update preferences - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - } - } -} - -// Updates "Time Left" toolbar item -// -LeechBlock.updateTimeLeft = function (secsLeft) { - let timeLeft = document.getElementById("leechblock-time-left"); - if (timeLeft == null) { - return; - } - - if (secsLeft == undefined || secsLeft == Infinity) { - timeLeft.value = "--:--:--"; - timeLeft.style.backgroundColor = "#BBB"; - timeLeft.style.color = "#444"; - timeLeft.hidden = LeechBlock.getBoolPref("htl"); - } else { - timeLeft.value = LeechBlock.formatTime(secsLeft); - timeLeft.style.backgroundColor = "#FFF"; - timeLeft.style.color = "#000"; - timeLeft.hidden = false; - } -} - -// Handles countdown on delayed block page -// -LeechBlock.onCountdownTimer = function (countdown) { - let win = countdown.win; - let doc = win.document; - - // Advance countdown if window has focus - if (doc.hasFocus()) { - countdown.delaySecs--; - } - - // Update countdown seconds on page - let secondsSpan = doc.getElementById("leechblockDelaySecondsSpan"); - if (secondsSpan != null) { - secondsSpan.textContent = countdown.delaySecs; - } - - if (countdown.delaySecs == 0) { - // Clear countdown timer - clearInterval(doc.leechblockCountdownInterval); - doc.leechblockCountdownInterval = undefined; - - // Get parsed URL for blocked page - let parsedURL = LeechBlock.getParsedURL(countdown.blockedURL); - - // Set preference for allowed origin/page - if (LeechBlock.getBitPref("delayFirst", countdown.blockedSet)) { - LeechBlock.setUniCharPref("ao", parsedURL.origin); - LeechBlock.clearUserPref("ap"); - } else { - LeechBlock.setUniCharPref("ap", parsedURL.page); - LeechBlock.clearUserPref("ao"); - } - - // Continue to blocked page - win.location = countdown.blockedURL; - } -} - -// Opens options dialog -// -LeechBlock.openOptionsDialog = function () { - window.openDialog("chrome://leechblock/content/options.xul", - "leechblock-options", "chrome,centerscreen"); -} - -// Opens statistics dialog -// -LeechBlock.openStatsDialog = function () { - window.openDialog("chrome://leechblock/content/stats.xul", - "leechblock-stats", "chrome,centerscreen"); -} - -// Opens lockdown dialog -// -LeechBlock.openLockdownDialog = function () { - window.openDialog("chrome://leechblock/content/lockdown.xul", - "leechblock-lockdown", "chrome,centerscreen"); -} - -// Shows alert dialog with block warning -// -LeechBlock.alertBlockWarning = function (set, setName, secsLeft) { - if (setName == "") { - setName = LeechBlock.locale_blockSet + " " + set; - } - LeechBlock.PROMPTS.alert(null, - LeechBlock.locale_warning_title, - LeechBlock.locale_warning_alertBlock - .replace(/\$B/, setName) - .replace(/\$S/, secsLeft)); -} - -// Returns label for item for add site menu -// -LeechBlock.getAddSiteMenuItemLabel = function (site, set, setName) { - if (setName != "") { - return LeechBlock.locale_menu_addThisSiteLabel - .replace(/\$S/, site) - .replace(/\$B/, setName); - } else { - return LeechBlock.locale_menu_addThisSiteLabel - .replace(/\$S/, site) - .replace(/\$B/, LeechBlock.locale_blockSet + " " + set); - } -} - -// Prepares menu (either context menu or toolbar menu) -// -LeechBlock.prepareMenu = function (menu, win) { - // Remove all menu items except last three - while (menu.children.length > 3) { - menu.removeChild(menu.firstChild); - } - - // Get parsed URL for current page - let parsedURL = LeechBlock.getParsedURL(win.location.href); - - // Quick exit for non-http URLs - if (!/^http/.test(parsedURL.protocol)) { - return; - } - - // Get site name from URL - let site = parsedURL.host.replace(/^www\./, ""); - - // Add separator element - let menuseparator = document.createElementNS( - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - "menuseparator"); - menu.insertBefore(menuseparator, menu.firstChild); - - // Add menu item for each block set - for (let set = 1; set <= 6; set++) { - // Get custom block set name (if specified) - let setName = LeechBlock.getUniCharPref("setName" + set); - - // Create new menu item - let menuitem = document.createElementNS( - "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", - "menuitem"); - menuitem.setAttribute("id", "leechblock-context-menuitem-addsite" + set); - menuitem.setAttribute("label", - LeechBlock.getAddSiteMenuItemLabel(site, set, setName)); - menuitem.site = site; - menuitem.set = set; - - // Add menu item before separator - menu.insertBefore(menuitem, menuseparator); - } -} - -// Adds site to block set -// -LeechBlock.addSiteToSet = function (menu, win) { - // Get site name and set number from menu item - let site = menu.site; - let set = menu.set; - - if (site == undefined || set == undefined) { - return; - } - - // Get parsed URL for current page - let parsedURL = LeechBlock.getParsedURL(win.location.href); - - // Get sites for this set - let sites = LeechBlock.getUniCharPref("sites" + set); - - // Add site if not already included - let patterns = sites.split(/\s+/); - if (patterns.indexOf(site) < 0) { - if (sites == "") { - sites = site; - } else { - sites += " " + site; - } - - // Get regular expressions to match sites - let regexps = LeechBlock.getRegExpSites(sites); - - // Update preferences - LeechBlock.setUniCharPref("sites" + set, sites); - LeechBlock.setUniCharPref("blockRE" + set, regexps.block); - LeechBlock.setUniCharPref("allowRE" + set, regexps.allow); - LeechBlock.setUniCharPref("keywordRE" + set, regexps.keyword); - - LeechBlock.checkWindow(parsedURL, win, false); - - LeechBlock.updateTimeLeft(win.leechblockSecsLeft); - } -} - -// Hides extension in about:addons -// -LeechBlock.hideExtension = function (doc) { - let lists = [ - "search-list", // Search - "addon-list", // Extensions - "updates-list", // Recent Updates - ]; - - for (let list of lists) { - let richlistbox = doc.getElementById(list); - if (richlistbox != null) { - let elements = richlistbox.getElementsByAttribute("value", LeechBlock.ID); - for (let i = 0; i < elements.length; i++) { - elements[i].hidden = true; - } - } - } - - // Repeat after short interval (list is repopulated whenever category selection is changed) - doc.leechblockAddonsTimeout = setTimeout( - LeechBlock.hideExtension, - 200, doc); -} - -// Returns time when blocked sites will be unblocked (as localized string) -// -LeechBlock.getUnblockTime = function (set) { - // Get current time/date - let timedate = new Date(); - - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - // Get preferences for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let times = LeechBlock.getCharPref("times" + set); - let minPeriods = LeechBlock.getMinPeriods(times); - let limitMins = LeechBlock.getCharPref("limitMins" + set); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let periodStart = LeechBlock.getTimePeriodStart(now, limitPeriod); - let conjMode = LeechBlock.getBitPref("conjMode", set); - let days = LeechBlock.getIntPref("days" + set); - let daySel = LeechBlock.decodeDays(days); - - // Check for valid time data - if (timedata.length < 5) { - return null; - } - - // Check for 24/7 block - if (times == LeechBlock.ALL_DAY_TIMES && days == 127 && !conjMode) { - return null; - } - - // Check for lockdown - if (now < timedata[4]) { - // Return end time for lockdown - return new Date(timedata[4] * 1000); - } - - // Get number of minutes elapsed since midnight - let mins = timedate.getHours() * 60 + timedate.getMinutes(); - - // Create list of time periods for today and following seven days - let day = timedate.getDay(); - let allMinPeriods = []; - for (let i = 0; i <= 7; i++) { - if (daySel[(day + i) % 7]) { - let offset = (i * 1440); - for (let mp of minPeriods) { - // Create new time period with offset - let mp1 = { - start: (mp.start + offset), - end: (mp.end + offset) - }; - if (allMinPeriods.length == 0) { - // Add new time period - allMinPeriods.push(mp1); - } else { - mp0 = allMinPeriods[allMinPeriods.length - 1]; - if (mp1.start <= mp0.end) { - // Merge time period into previous one - mp0.end = mp1.end; - } else { - // Add new time period - allMinPeriods.push(mp1); - } - } - } - } - } - - let timePeriods = (times != ""); - let timeLimit = (limitMins != "" && limitPeriod != ""); - - if (timePeriods && !timeLimit) { - // Case 1: within time periods (no time limit) - - // Find relevant time period - for (let mp of allMinPeriods) { - if (mins >= mp.start && mins < mp.end) { - // Return end time for time period - return new Date( - timedate.getFullYear(), - timedate.getMonth(), - timedate.getDate(), - 0, mp.end); - } - } - } else if (!timePeriods && timeLimit) { - // Case 2: after time limit (no time periods) - - // Return end time for current time limit period - return new Date(timedata[2] * 1000 + limitPeriod * 1000); - } else if (timePeriods && timeLimit) { - if (conjMode) { - // Case 3: within time periods AND after time limit - - // Find relevant time period - for (let mp of allMinPeriods) { - if (mins >= mp.start && mins < mp.end) { - // Return the earlier of the two end times - let td1 = new Date( - timedate.getFullYear(), - timedate.getMonth(), - timedate.getDate(), - 0, mp.end); - let td2 = new Date(timedata[2] * 1000 + limitPeriod * 1000); - return (td1 < td2) ? td1 : td2; - } - } - } else { - // Case 4: within time periods OR after time limit - - // Determine whether time limit was exceeded - let afterTimeLimit = (timedata[2] == periodStart) - && (timedata[3] >= (limitMins * 60)); - - if (afterTimeLimit) { - // Check against end time for current time limit period instead - let td = new Date(timedata[2] * 1000 + limitPeriod * 1000); - mins = td.getHours() * 60 + td.getMinutes(); - } - - // Find relevant time period - for (let mp of allMinPeriods) { - if (mins >= mp.start && mins < mp.end) { - // Return end time for time period - return new Date( - timedate.getFullYear(), - timedate.getMonth(), - timedate.getDate(), - 0, mp.end); - } - } - } - } - - return null; -} - -// Add listeners for browser loading/unloading and page loading -window.addEventListener("load", LeechBlock.onLoad, false); -window.addEventListener("unload", LeechBlock.onUnload, false); -window.addEventListener("DOMContentLoaded", LeechBlock.onPageLoad, false); diff --git a/chrome/content/browser.xul b/chrome/content/browser.xul deleted file mode 100644 index d517155..0000000 --- a/chrome/content/browser.xul +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/const.js b/chrome/content/const.js deleted file mode 100644 index 36c3d74..0000000 --- a/chrome/content/const.js +++ /dev/null @@ -1,3 +0,0 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; diff --git a/chrome/content/core.js b/chrome/content/core.js deleted file mode 100644 index a3001cb..0000000 --- a/chrome/content/core.js +++ /dev/null @@ -1,539 +0,0 @@ -/* 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/. */ - -/* - * This file defines the global namespace object and various utility functions. - */ - -// Global namespace object -// -if (typeof LeechBlock == "undefined") { - var LeechBlock = { - // Flags to keep track of which block sets have had warnings displayed - doneWarning: [false, false, false, false, false, false], - // Lists to keep track of loaded documents - loadedDocs: [[], [], [], [], [], []] - }; -} - -// Various quasi-constants -// -LeechBlock.ID = "{a95d8332-e4b4-6e7f-98ac-20b733364387}"; -LeechBlock.ALL_DAY_TIMES = "0000-2400"; -LeechBlock.BROWSER_URL = "chrome://browser/content/browser.xul"; -LeechBlock.DEFAULT_BLOCK_URL = "chrome://leechblock/content/blocked.xhtml?$S&$U"; -LeechBlock.DELAYED_BLOCK_URL = "chrome://leechblock/content/delayed.xhtml?$S&$U"; -LeechBlock.PREFS = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefService).getBranch("extensions.leechblock."); -LeechBlock.PROMPTS = Cc["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Ci.nsIPromptService); - -// Functions to access user preferences -// -LeechBlock.clearUserPref = LeechBlock.PREFS.clearUserPref; -LeechBlock.getBoolPref = LeechBlock.PREFS.getBoolPref; -LeechBlock.setBoolPref = LeechBlock.PREFS.setBoolPref; -LeechBlock.getCharPref = LeechBlock.PREFS.getCharPref; -LeechBlock.setCharPref = LeechBlock.PREFS.setCharPref; -LeechBlock.getIntPref = LeechBlock.PREFS.getIntPref; -LeechBlock.setIntPref = LeechBlock.PREFS.setIntPref; -LeechBlock.getUniCharPref = function (name) { - return LeechBlock.PREFS.getComplexValue(name, Ci.nsISupportsString).data; -}; -LeechBlock.setUniCharPref = function (name, value) { - let str = Cc["@mozilla.org/supports-string;1"] - .createInstance(Ci.nsISupportsString); - str.data = value; - LeechBlock.PREFS.setComplexValue(name, Ci.nsISupportsString, str); -}; -LeechBlock.getBitPref = function (name, bit) { - return ((LeechBlock.PREFS.getIntPref(name) & (1 << --bit)) != 0); -} -LeechBlock.setBitPref = function (name, bit, value) { - let bits = LeechBlock.PREFS.getIntPref(name); - if (value) { - LeechBlock.PREFS.setIntPref(name, bits | (1 << --bit)); - } else { - LeechBlock.PREFS.setIntPref(name, bits & ~(1 << --bit)); - } -} - -// Adds loaded document to list for specified block set -// (returns true if document added, false if already in list) -// -LeechBlock.addLoadedDoc = function (set, doc) { - let docs = LeechBlock.loadedDocs[set - 1]; - for (let i = 0; i < docs.length; i++) { - if (docs[i] == doc) { - return false; - } - } - docs.unshift(doc); - return true; -} - -// Removes loaded document from list for specified block set -// (returns true if document removed, false if not in list) -// -LeechBlock.removeLoadedDoc = function (set, doc) { - let docs = LeechBlock.loadedDocs[set - 1]; - for (let i = 0; i < docs.length; i++) { - if (docs[i] == doc) { - docs.splice(i, 1); - return true; - } - } - return false; -} - -// Sets active loaded document for specified block set -// (returns true if successful) -// -LeechBlock.setActiveLoadedDoc = function (set, doc) { - let docs = LeechBlock.loadedDocs[set - 1]; - if (docs.length == 0) { - return false; // list is empty - } else if (docs[0] == doc) { - return true; // already in first place - } else { - for (let i = 1; i < docs.length; i++) { - if (docs[i] == doc) { - docs.splice(i, 1); - docs.unshift(doc); - return true; // moved to first place - } - } - return false; // not in list - } -} - -// Checks for active loaded document for specified block set -// -LeechBlock.isActiveLoadedDoc = function (set, doc) { - let docs = LeechBlock.loadedDocs[set - 1]; - return (docs.length == 0) ? false : (docs[0] == doc); -} - -// Returns number of loaded documents for specified block set -// -LeechBlock.numLoadedDocs = function (set) { - return LeechBlock.loadedDocs[set - 1].length; -} - -// Saves all preferences to file -// -LeechBlock.savePreferences = function () { - Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefService) - .savePrefFile(null); -} - -// Cleans preferences -// -LeechBlock.cleanPreferences = function () { - for (let set = 1; set <= 6; set++) { - // Clean time periods - let times = LeechBlock.getUniCharPref("times" + set); - LeechBlock.setUniCharPref("times" + set, LeechBlock.cleanTimePeriods(times)); - } -} - -// Returns parsed URL (page address, arguments, and hash) -// -LeechBlock.getParsedURL = function (url) { - const PARSE_URL = /^(((\w+):\/*(\w+(?::\w+)?@)?([\w-\.]+)(?::(\d*))?)([^\?#]*))(\?[^#]*)?(#.*)?$/; - - let results = PARSE_URL.exec(url); - if (results != null) { - let page = results[1]; - let origin = results[2]; - let protocol = results[3]; - let userinfo = results[4]; - let host = results[5]; - let port = results[6]; - let path = results[7]; - let query = results[8]; - let fragment = results[9]; - return { - pageNoArgs: page, - page: (query == null) ? page : (page + query), - origin: origin, - protocol: protocol, - host: host, - path: path, - query: query, - args: (query == null) ? null : query.substring(1).split(/[;&]/), - hash: (fragment == null) ? null : fragment.substring(1) - }; - } else { - console.warn("[LB] Cannot parse URL: " + url); - return { - pageNoArgs: null, - page: null, - origin: null, - protocol: null, - host: null, - path: null, - query: null, - args: null, - hash: null - }; - } -} - -// Returns browser version -// -LeechBlock.getBrowserVersion = function () { - return Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefService) - .getCharPref("extensions.lastAppVersion"); -} - -// Creates regular expressions for matching sites to block/allow -// -LeechBlock.getRegExpSites = function (sites) { - if (sites == "") { - return { - block: "", - allow: "", - keyword: "" - }; - } - - let blockFiles = false; - let allowFiles = false; - - let patterns = sites.split(/\s+/); - let blocks = []; - let allows = []; - let keywords = []; - for (let pattern of patterns) { - if (pattern == "FILE") { - blockFiles = true; - } else if (pattern == "+FILE") { - allowFiles = true; - } else if (pattern.charAt(0) == "~") { - // Add a keyword - keywords.push(LeechBlock.keywordToRegExp(pattern.substr(1))); - } else if (pattern.charAt(0) == "+") { - // Add a regexp to allow site(s) as exception(s) - allows.push(LeechBlock.patternToRegExp(pattern.substr(1))); - } else if (pattern.charAt(0) != "#") { - // Add a regexp to block site(s) - blocks.push(LeechBlock.patternToRegExp(pattern)); - } - } - return { - block: (blocks.length > 0) - ? "^" + (blockFiles ? "file:|" : "") + "(https?|file):\\/+(" + blocks.join("|") + ")" - : (blockFiles ? "^file:" : ""), - allow: (allows.length > 0) - ? "^" + (allowFiles ? "file:|" : "") + "(https?|file):\\/+(" + allows.join("|") + ")" - : (allowFiles ? "^file:" : ""), - keyword: (keywords.length > 0) ? keywords.join("|") : "" - }; -} - -// Converts site pattern to regular expression -// -LeechBlock.patternToRegExp = function (pattern) { - let special = /[\.\|\?\:\+\-\^\$\(\)\[\]\{\}\\]/g; - return "(www\\.)?" + pattern // assume optional www prefix - .replace(special, "\\$&") // fix special chars - .replace(/^www\\\./, "") // remove existing www prefix - .replace(/\*{2,}/g, ".{STAR}") // convert super-wildcards - .replace(/\*/g, "[^\\/]{STAR}") // convert wildcards - .replace(/{STAR}/g, "*"); // convert stars -} - -// Converts keyword to regular expression -// -LeechBlock.keywordToRegExp = function (keyword) { - let special = /[\.\|\?\:\+\-\^\$\(\)\[\]\{\}\\]/g; - return "\\b" + keyword - .replace(special, "\\$&") // fix special chars - .replace(/_+/g, "\\s+") // convert underscores - .replace(/\*+/, "\\S*") // convert wildcards - + "\\b"; -} - -// Tests URL against block/allow regular expressions -// -LeechBlock.testURL = function (pageURL, blockRE, allowRE) { - return (blockRE != "" && (new RegExp(blockRE, "i")).test(pageURL) - && !(allowRE != "" && (new RegExp(allowRE, "i")).test(pageURL))); -} - -// Checks time periods format -// -LeechBlock.checkTimePeriodsFormat = function (times) { - return (times == "") || /^[0-2]\d[0-5]\d-[0-2]\d[0-5]\d([, ]+[0-2]\d[0-5]\d-[0-2]\d[0-5]\d)*$/.test(times); -} - -// Checks positive integer format -// -LeechBlock.checkPosIntFormat = function (value) { - return (value == "") || /^[1-9][0-9]*$/.test(value); -} - -// Converts times to minute periods -// -LeechBlock.getMinPeriods = function (times) { - let minPeriods = []; - if (times != "") { - let regexp = /^(\d\d)(\d\d)-(\d\d)(\d\d)$/; - let periods = times.split(/[, ]+/); - for (let period of periods) { - let results = regexp.exec(period); - if (results != null) { - let minPeriod = { - start: (parseInt(results[1], 10) * 60 + parseInt(results[2], 10)), - end: (parseInt(results[3], 10) * 60 + parseInt(results[4], 10)) - }; - minPeriods.push(minPeriod); - } - } - } - return minPeriods; -} - -// Cleans time periods -// -LeechBlock.cleanTimePeriods = function (times) { - // Convert to minute periods - let minPeriods = LeechBlock.getMinPeriods(times); - if (minPeriods.length == 0) { - return ""; // nothing to do - } - - // Step 1: Fix any times > 2400 - for (let mp of minPeriods) { - mp.start = Math.min(mp.start, 1440); - mp.end = Math.min(mp.end, 1440); - } - - // Step 2: Remove any periods without +ve duration - for (let i = 0; i < minPeriods.length; i++) { - if (minPeriods[i].start >= minPeriods[i].end) { - minPeriods.splice(i--, 1); - } - } - - // Step 3: Sort periods in order of start time - minPeriods.sort(function (a, b) { return (a.start - b.start); }); - - // Step 4: Combine overlapping periods - for (let i = 0; i < (minPeriods.length - 1); i++) { - let mp1 = minPeriods[i]; - let mp2 = minPeriods[i + 1]; - if (mp2.start <= mp1.end) { - // Merge first period into second period (and back up index) - mp2.start = mp1.start; - mp2.end = Math.max(mp1.end, mp2.end); - minPeriods.splice(i--, 1); - } - } - - // Convert back to string list of time periods - let cleanTimes = []; - for (let mp of minPeriods) { - let h1 = Math.floor(mp.start / 60); - let m1 = (mp.start % 60); - let h2 = Math.floor(mp.end / 60); - let m2 = (mp.end % 60); - let period = - ((h1 < 10) ? "0" : "") + h1 + - ((m1 < 10) ? "0" : "") + m1 + - "-" + - ((h2 < 10) ? "0" : "") + h2 + - ((m2 < 10) ? "0" : "") + m2; - cleanTimes.push(period); - } - return cleanTimes.join(","); -} - -// Encodes day selection -// -LeechBlock.encodeDays = function (daySel) { - let days = 0; - for (let i = 0; i < 7; i++) { - if (daySel[i]) days |= (1 << i); - } - return days; -} - -// Decodes day selection -// -LeechBlock.decodeDays = function (days) { - let daySel = new Array(7); - for (let i = 0; i < 7; i++) { - daySel[i] = ((days & (1 << i)) != 0); - } - return daySel; -} - -// Calculates start of time period from current time and time limit period -// -LeechBlock.getTimePeriodStart = function (now, limitPeriod) { - limitPeriod = +limitPeriod; // force value to number - - if (limitPeriod > 0) { - let periodStart = now - (now % limitPeriod); - - // Adjust start time for timezone, DST, and Sunday as first day of week - if (limitPeriod > 3600) { - let offsetMins = new Date(now * 1000).getTimezoneOffset(); - periodStart += offsetMins * 60; // add time difference - if (limitPeriod > 86400) { - periodStart -= 345600; // subtract four days (Thu back to Sun) - } - - // Correct any boundary errors - while (periodStart > now) { - periodStart -= limitPeriod; - } - while (periodStart <= now - limitPeriod) { - periodStart += limitPeriod; - } - } - - return periodStart; - } - - return 0; -} - -// Formats a time in seconds to HH:MM:SS format -// -LeechBlock.formatTime = function (time) { - let neg = (time < 0); - time = Math.abs(time); - let h = Math.floor(time / 3600); - let m = Math.floor(time / 60) % 60; - let s = Math.floor(time) % 60; - return (neg ? "-" : "") + ((h < 10) ? "0" + h : h) - + ":" + ((m < 10) ? "0" + m : m) - + ":" + ((s < 10) ? "0" + s : s); -} - -// Reads UTF-8 text file -// -LeechBlock.readTextFile = function (file) { - const charSet = "UTF-8"; - const bufferSize = 4096; - const replaceChar = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER; - - // Create UTF-8 file input stream - let fis = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); - fis.init(file, 0x01, 0664, 0); - let cis = Cc["@mozilla.org/intl/converter-input-stream;1"] - .createInstance(Ci.nsIConverterInputStream); - cis.init(fis, charSet, bufferSize, replaceChar); - - // Read all text from stream - let text = ""; - let str = {}; - while (cis.readString(bufferSize, str) != 0) { - text += str.value; - } - - // Close input stream - cis.close(); - fis.close(); - - return text; -} - -// Writes UTF-8 text file -// -LeechBlock.writeTextFile = function (file, text) { - const charSet = "UTF-8"; - const bufferSize = 4096; - const replaceChar = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER; - - // Create UTF-8 file output stream - let fos = Cc["@mozilla.org/network/file-output-stream;1"] - .createInstance(Ci.nsIFileOutputStream); - fos.init(file, 0x02 | 0x08 | 0x20, 0664, 0); - let cos = Cc["@mozilla.org/intl/converter-output-stream;1"] - .createInstance(Ci.nsIConverterOutputStream); - cos.init(fos, charSet, bufferSize, replaceChar); - - // Write text to stream - cos.writeString(text); - - // Close output stream - cos.close(); - fos.close(); -} - -// Creates a random access code of a specified length -// -LeechBlock.createAccessCode = function (len) { - // Omit O, 0, I, l to avoid ambiguity with some fonts - const codeChars = "!@#$%^&*()[]{}/\<>?+-=ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz123456789"; - let code = ""; - for (let i = 0; i < len; i++) { - code += codeChars.charAt(Math.random() * codeChars.length); - } - return code; -} - -// Retrieves options password from Login Manager -// -LeechBlock.retrievePassword = function () { - try { - const loginManager = Cc["@mozilla.org/login-manager;1"] - .getService(Ci.nsILoginManager); - - const hostname = "chrome://leechblock"; - const httprealm = "Options"; - const username = ""; - - // Search for password - let logins = loginManager.findLogins({}, hostname, null, httprealm); - for (let i = 0; i < logins.length; i++) { - if (logins[i].username == username) { - return logins[i].password; - } - } - } catch (e) { - console.warn("[LB] Cannot retrieve password: " + e.toString()); - if (e.result == Cr.NS_ERROR_ABORT) { - return null; // user canceled master password entry - } - } - - return ""; // no password found -} - -// Stores options password in Login Manager -// -LeechBlock.storePassword = function (password) { - try { - const loginManager = Cc["@mozilla.org/login-manager;1"] - .getService(Ci.nsILoginManager); - - const hostname = "chrome://leechblock"; - const httprealm = "Options"; - const username = ""; - - // Remove any existing password - let logins = loginManager.findLogins({}, hostname, null, httprealm); - for (let i = 0; i < logins.length; i++) { - loginManager.removeLogin(logins[i]); - } - - // Add new password - if (password != null && password != "") { - let login = Cc["@mozilla.org/login-manager/loginInfo;1"] - .createInstance(Ci.nsILoginInfo); - login.init(hostname, null, httprealm, username, password, "", ""); - loginManager.addLogin(login); - } - } catch (e) { - console.warn("[LB] Cannot store password: " + e.toString()); - } -} diff --git a/chrome/content/delayed.xhtml b/chrome/content/delayed.xhtml deleted file mode 100644 index 6eb60b6..0000000 --- a/chrome/content/delayed.xhtml +++ /dev/null @@ -1,50 +0,0 @@ - - %xhtmlDTD; - - %leechblockDTD; -]> - - - - - &siteBlocked; - - - - - - - - - - -
- &leechblock; logo -
- - -
-

&pageAccessDelayed1;&leechblock;&pageAccessDelayed2;

-
- - -
- - (&blockSet;) -

&pageWillBeLoaded1;&pageWillBeLoaded2;

-
- - -
- Want to block distractions on other devices and browsers? Check out Freedom!
- Schedule and sync your blocking sessions across Mac, Windows, and iOS devices.
-

Freedom logo

-
- - -
&considerSupport1;
&considerSupport2;&considerSupport3;&considerSupport4;
- - - - diff --git a/chrome/content/delayed_no_ad.xhtml b/chrome/content/delayed_no_ad.xhtml deleted file mode 100644 index cfb0e24..0000000 --- a/chrome/content/delayed_no_ad.xhtml +++ /dev/null @@ -1,43 +0,0 @@ - - %xhtmlDTD; - - %leechblockDTD; -]> - - - - - &siteBlocked; - - - - - - - - - - -
- &leechblock; logo -
- - -
-

&pageAccessDelayed1;&leechblock;&pageAccessDelayed2;

-
- - -
- - (&blockSet;) -

&pageWillBeLoaded1;&pageWillBeLoaded2;

-
- - -
&considerSupport1;
&considerSupport2;&considerSupport3;&considerSupport4;
- - - - diff --git a/chrome/content/fonts.css b/chrome/content/fonts.css deleted file mode 100644 index 5858fa8..0000000 --- a/chrome/content/fonts.css +++ /dev/null @@ -1,47 +0,0 @@ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url('fonts/Open-Sans-Light.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url('fonts/Open-Sans-Light-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url('fonts/Open-Sans.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url('fonts/Open-Sans-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: local('Open Sans Italic'), local('OpenSans-Italic'), url('fonts/Open-Sans-Italic.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: local('Open Sans Italic'), local('OpenSans-Italic'), url('fonts/Open-Sans-Italic-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} diff --git a/chrome/content/fonts/LICENSE b/chrome/content/fonts/LICENSE deleted file mode 100644 index d645695..0000000 --- a/chrome/content/fonts/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/chrome/content/fonts/Open-Sans-Italic-ext.woff2 b/chrome/content/fonts/Open-Sans-Italic-ext.woff2 deleted file mode 100644 index 72208f35fbd5bd86d461e5ca4aa98e2b67c3c37b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11908 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

+4w|L%I=3)MA4xL7mmMmgt~Ox-MW@N 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 PmYw*n#pv{ZUb6oOt^@7h diff --git a/chrome/content/fonts/Open-Sans-Italic.woff2 b/chrome/content/fonts/Open-Sans-Italic.woff2 deleted file mode 100644 index a739e6ab026cd8995672413966200e07bebd2d14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14968 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 diff --git a/chrome/content/fonts/Open-Sans-Light-ext.woff2 b/chrome/content/fonts/Open-Sans-Light-ext.woff2 deleted file mode 100644 index 3e198c19ab83c3fb2858cd0f07a626dbe99cde5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12412 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 xyud-ljDf$}5bEl=-({vT?-@DTt2 diff --git a/chrome/content/fonts/Open-Sans-Light.woff2 b/chrome/content/fonts/Open-Sans-Light.woff2 deleted file mode 100644 index 96ae7edd3e4d4bdf60e0adc50a816c31641bbc62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16152 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 z 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 Jm7n+l{}1w);gbLW diff --git a/chrome/content/fonts/Open-Sans-ext.woff2 b/chrome/content/fonts/Open-Sans-ext.woff2 deleted file mode 100644 index 0b5a66eb4ca09f9562bbec0f01a11c271eb1ede3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 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 R`Zu5MH=Po%BdC`F{T~T|rpW*R diff --git a/chrome/content/fonts/Open-Sans.woff2 b/chrome/content/fonts/Open-Sans.woff2 deleted file mode 100644 index 5287058cf90dd427ca8ad6e330cd339bd257dd9c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15572 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 ab)kb)z25LcH12#3l+gg_#ocOw0{nk#e8;T- diff --git a/chrome/content/lockdown.js b/chrome/content/lockdown.js deleted file mode 100644 index 5e4b61b..0000000 --- a/chrome/content/lockdown.js +++ /dev/null @@ -1,112 +0,0 @@ -/* 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/. */ - -/* - * This file contains the code for the Lockdown dialog. - */ - -// Handles lockdown dialog initialization -// -LeechBlock.lockdownInit = function () { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - // Check whether a lockdown is currently active - let endTime = 0; - for (let set = 1; set <= 6; set++) { - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - endTime = Math.max(endTime, timedata[4]); - } - } - if (endTime > now) { - // Show alert dialog with end time - LeechBlock.alertLockdown(new Date(endTime * 1000).toLocaleString()); - // Close lockdown dialog - window.close(); - } - - // Get preferences - let duration = LeechBlock.getIntPref("lockdownDuration"); - let sets = LeechBlock.getIntPref("lockdownSets"); - - // Set component values - let hours = Math.floor(duration / 3600); - let mins = Math.floor(duration / 60) % 60; - document.getElementById("lb-lockdown-hours").value = hours; - document.getElementById("lb-lockdown-mins").value = mins; - for (let set = 1; set <= 6; set++) { - let lockdown = (sets & (1 << (set - 1))) != 0; - document.getElementById("lb-lockdown-set" + set).checked = lockdown; - document.getElementById("lb-lockdown-set" + set).label += " " - + LeechBlock.getBlockSetName(set); - } -} - -// Handles lockdown dialog OK button -// -LeechBlock.lockdownOK = function () { - // Get component values - let hours = document.getElementById("lb-lockdown-hours").value; - let mins = document.getElementById("lb-lockdown-mins").value; - let duration = hours * 3600 + mins * 60; - let sets = 0; - for (let set = 1; set <= 6; set++) { - let lockdown = document.getElementById("lb-lockdown-set" + set).checked; - if (lockdown) sets |= (1 << (set - 1)); - } - - // Set preferences - LeechBlock.setIntPref("lockdownDuration", duration); - LeechBlock.setIntPref("lockdownSets", sets); - - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - // Update time data for selected block sets - let endTime = now + duration; - for (let set = 1; set <= 6; set++) { - let lockdown = document.getElementById("lb-lockdown-set" + set).checked; - - // Update time data for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[4] = lockdown ? endTime : 0; - } else { - timedata = [now, 0, 0, 0, lockdown ? endTime : 0]; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - } - - // Clear preference for allowed origin/page - LeechBlock.clearUserPref("ao"); - LeechBlock.clearUserPref("ap"); - - return true; -} - -// Handles lockdown dialog Cancel button -// -LeechBlock.lockdownCancel = function () { - return true; -} - -// Shows alert dialog with end time -// -LeechBlock.alertLockdown = function (endTime) { - LeechBlock.PROMPTS.alert(null, - LeechBlock.locale_lockdown_title, - LeechBlock.locale_lockdown_alertLockdown + " " + endTime); -} - -// Returns name of block set -// -LeechBlock.getBlockSetName = function (set) { - let setName = LeechBlock.getUniCharPref("setName" + set); - if (setName != "") { - return setName; - } else { - return LeechBlock.locale_blockSet + " " + set; - } -} diff --git a/chrome/content/lockdown.xul b/chrome/content/lockdown.xul deleted file mode 100644 index f51dd80..0000000 --- a/chrome/content/lockdown.xul +++ /dev/null @@ -1,92 +0,0 @@ - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/options.js b/chrome/content/options.js deleted file mode 100644 index 712b464..0000000 --- a/chrome/content/options.js +++ /dev/null @@ -1,823 +0,0 @@ -/* 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/. */ - -/* - * This file contains the code for the Options dialog. - */ - -// Handles options dialog initialization -// -LeechBlock.optionsInit = function () { - // Get current time/date - let timedate = new Date(); - - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - // Get password - let password = LeechBlock.retrievePassword(); - if (password == null) { - // Close options dialog - window.close(); - return; - } - document.getElementById("lb-options-password").value = password; - - // Get access preferences - let oa = LeechBlock.getIntPref("oa"); - let hpp = LeechBlock.getBoolPref("hpp"); - document.getElementById("lb-options-access").value = oa; - document.getElementById("lb-options-hpp").checked = hpp; - LeechBlock.updatePasswordOptions(); - - // Ask for password (if required) - if (oa == 1 && password != "" && password != LeechBlock.requestPassword(hpp)) { - // Close options dialog - window.close(); - return; - } - - // Ask for access code (if required) - if (oa >= 2 && oa <= 4) { - // Create random access code - let code = LeechBlock.createAccessCode(8 << oa); - // Get active window - let awin = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher).activeWindow; - // Open dialog for user to enter code (centred on active window) - let usercode = { value: null }; - awin.openDialog("chrome://leechblock/content/accesscode.xul", - "lb-accesscode", "chrome,centerscreen,dialog,modal", - code, usercode); - if (code != usercode.value) { - // Close options dialog - window.close(); - return; - } - } - - // Check whether a lockdown is currently active - for (let set = 1; set <= 6; set++) { - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let lockdown = (timedata.length == 5 && timedata[4] > now); - if (lockdown) { - // Enable 'Cancel Lockdown' button - document.getElementById("lb-cancel-lockdown" + set).disabled = false; - break; - } - } - - // Check whether access to options should be prevented - for (let set = 1; set <= 6; set++) { - if (LeechBlock.getBitPref("prevOpts", set)) { - // Get preferences - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let times = LeechBlock.getCharPref("times" + set); - let minPeriods = LeechBlock.getMinPeriods(times); - let limitMins = LeechBlock.getCharPref("limitMins" + set); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let periodStart = LeechBlock.getTimePeriodStart(now, limitPeriod); - let conjMode = LeechBlock.getBitPref("conjMode", set); - let daySel = LeechBlock.decodeDays(LeechBlock.getIntPref("days" + set)); - - // Check day - let onSelectedDay = daySel[timedate.getDay()]; - - // Check time periods - let withinTimePeriods = false; - if (onSelectedDay && times != "") { - // Get number of minutes elapsed since midnight - let mins = timedate.getHours() * 60 + timedate.getMinutes(); - - // Check each time period in turn - for (let mp of minPeriods) { - if (mins >= mp.start && mins < mp.end) { - withinTimePeriods = true; - } - } - } - - // Check time limit - let afterTimeLimit = false; - if (onSelectedDay && limitMins != "" && limitPeriod != "") { - // Check time data validity, time period, and time limit - if (timedata.length == 5 - && timedata[2] == periodStart - && timedata[3] >= (limitMins * 60)) { - afterTimeLimit = true; - } - } - - // Check lockdown condition - let lockdown = (timedata.length == 5 && timedata[4] > now); - - // Disable options if specified block conditions are fulfilled - if (lockdown - || (!conjMode && (withinTimePeriods || afterTimeLimit)) - || (conjMode && (withinTimePeriods && afterTimeLimit))) { - // Disable options for this set - LeechBlock.disableSetOptions(set); - } - } - } - - for (let set = 1; set <= 6; set++) { - // Get preferences - let setName = LeechBlock.getUniCharPref("setName" + set); - let sites = LeechBlock.getUniCharPref("sites" + set); - sites = sites.replace(/\s+/g, "\n"); - let sitesURL = LeechBlock.getUniCharPref("sitesURL" + set); - let activeBlock = LeechBlock.getBitPref("activeBlock", set); - let prevOpts = LeechBlock.getBitPref("prevOpts", set); - let prevAddons = LeechBlock.getBitPref("prevAddons", set); - let prevConfig = LeechBlock.getBitPref("prevConfig", set); - let countFocus = LeechBlock.getBitPref("countFocus", set); - let delayFirst = LeechBlock.getBitPref("delayFirst", set); - let delaySecs = LeechBlock.getCharPref("delaySecs" + set); - let times = LeechBlock.getCharPref("times" + set); - let limitMins = LeechBlock.getCharPref("limitMins" + set); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let conjMode = LeechBlock.getBitPref("conjMode", set); - let daySel = LeechBlock.decodeDays(LeechBlock.getIntPref("days" + set)); - let blockURL = LeechBlock.getUniCharPref("blockURL" + set); - - // Set component values - document.getElementById("lb-set-name" + set).value = setName; - document.getElementById("lb-sites" + set).value = sites; - document.getElementById("lb-sites-URL" + set).value = sitesURL; - document.getElementById("lb-active-block" + set).checked = activeBlock; - document.getElementById("lb-prev-opts" + set).checked = prevOpts; - document.getElementById("lb-prev-addons" + set).checked = prevAddons; - document.getElementById("lb-prev-config" + set).checked = prevConfig; - document.getElementById("lb-count-focus" + set).checked = countFocus; - document.getElementById("lb-delay-first" + set).checked = delayFirst; - document.getElementById("lb-delay-secs" + set).value = delaySecs; - document.getElementById("lb-times" + set).value = times; - document.getElementById("lb-limit-mins" + set).value = limitMins; - document.getElementById("lb-limit-period" + set).value = limitPeriod; - document.getElementById("lb-mode" + set).selectedIndex = conjMode ? 1 : 0; - for (let i = 0; i < 7; i++) { - document.getElementById("lb-day" + i + set).checked = daySel[i]; - } - document.getElementById("lb-block-page" + set).value = blockURL; - - if (setName != "") { - // Set custom label - document.getElementById("lb-tab-set" + set).label = setName; - } - } - - // Get other preferences - let ham = LeechBlock.getBoolPref("ham"); - let hsm = LeechBlock.getBoolPref("hsm"); - let warnSecs = LeechBlock.getCharPref("warnSecs"); - let bep = LeechBlock.getBoolPref("bep"); - let kpb = LeechBlock.getBoolPref("kpb"); - let hcm = LeechBlock.getBoolPref("hcm"); - let htl = LeechBlock.getBoolPref("htl"); - document.getElementById("lb-options-ham").checked = ham; - document.getElementById("lb-options-hsm").checked = hsm; - document.getElementById("lb-options-warn-secs").value = warnSecs; - document.getElementById("lb-options-bep").checked = bep; - document.getElementById("lb-options-kpb").checked = kpb; - document.getElementById("lb-options-hcm").checked = hcm; - document.getElementById("lb-options-htl").checked = htl; -} - -// Handles options dialog OK button -// -LeechBlock.optionsOK = function () { - // Check format for time periods and time limits - for (let set = 1; set <= 6; set++) { - // Get component values - let times = document.getElementById("lb-times" + set).value; - let limitMins = document.getElementById("lb-limit-mins" + set).value; - let delaySecs = document.getElementById("lb-delay-secs" + set).value; - - // Check values - if (!LeechBlock.checkTimePeriodsFormat(times)) { - LeechBlock.setOuterTab(set - 1); - LeechBlock.setInnerTab(set, 1); - LeechBlock.alertBadTimes(); - document.getElementById("lb-times" + set).focus(); - return false; - } - if (!LeechBlock.checkPosIntFormat(limitMins)) { - LeechBlock.setOuterTab(set - 1); - LeechBlock.setInnerTab(set, 1); - LeechBlock.alertBadTimeLimit(); - document.getElementById("lb-limit-mins" + set).focus(); - return false; - } - if (!LeechBlock.checkPosIntFormat(delaySecs) || delaySecs == "") { - LeechBlock.setOuterTab(set - 1); - LeechBlock.setInnerTab(set, 2); - LeechBlock.alertBadSeconds(); - document.getElementById("lb-delay-secs" + set).focus(); - return false; - } - } - - // Check format for seconds before warning message - let warnSecs = document.getElementById("lb-options-warn-secs").value; - if (!LeechBlock.checkPosIntFormat(warnSecs)) { - LeechBlock.setOuterTab(7); - LeechBlock.alertBadSeconds(); - document.getElementById("lb-options-warn-secs").focus(); - return false; - } - - // Confirm settings where access to options is prevented all day - for (let set = 1; set <= 6; set++) { - // Get component values - let prevOpts = document.getElementById("lb-prev-opts" + set).checked; - let times = document.getElementById("lb-times" + set).value; - - if (!document.getElementById("lb-prev-opts" + set).disabled - && prevOpts && times == LeechBlock.ALL_DAY_TIMES) { - LeechBlock.setOuterTab(set - 1); - LeechBlock.setInnerTab(set, 1); - if (!LeechBlock.confirmPrevOptsAllDay()) { - document.getElementById("lb-times" + set).focus(); - return false; - } - } - } - - for (let set = 1; set <= 6; set++) { - // Get component values - let setName = document.getElementById("lb-set-name" + set).value; - let sites = document.getElementById("lb-sites" + set).value; - sites = sites.replace(/\s+/g, " ").replace(/(^ +)|( +$)|(\w+:\/+)/g, ""); - sites = sites.split(" ").sort().join(" "); // sort alphabetically - let sitesURL = document.getElementById("lb-sites-URL" + set).value; - let activeBlock = document.getElementById("lb-active-block" + set).checked; - let prevOpts = document.getElementById("lb-prev-opts" + set).checked; - let prevAddons = document.getElementById("lb-prev-addons" + set).checked; - let prevConfig = document.getElementById("lb-prev-config" + set).checked; - let countFocus = document.getElementById("lb-count-focus" + set).checked; - let delayFirst = document.getElementById("lb-delay-first" + set).checked; - let delaySecs = document.getElementById("lb-delay-secs" + set).value; - let times = document.getElementById("lb-times" + set).value; - let limitMins = document.getElementById("lb-limit-mins" + set).value; - let limitPeriod = document.getElementById("lb-limit-period" + set).value; - let conjMode = document.getElementById("lb-mode" + set).selectedIndex == 1; - let daySel = new Array(7); - for (let i = 0; i < 7; i++) { - daySel[i] = document.getElementById("lb-day" + i + set).checked; - } - let blockURL = document.getElementById("lb-block-page" + set).value; - - // Get regular expressions to match sites - let regexps = LeechBlock.getRegExpSites(sites); - - // Reset time data if time limit period has been changed - if (limitPeriod != LeechBlock.getCharPref("limitPeriod" + set)) { - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[2] = 0; - timedata[3] = 0; - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - } - } - - // Set preferences - LeechBlock.setUniCharPref("setName" + set, setName); - LeechBlock.setUniCharPref("sites" + set, sites); - LeechBlock.setUniCharPref("sitesURL" + set, sitesURL); - LeechBlock.setBitPref("activeBlock", set, activeBlock); - LeechBlock.setBitPref("prevOpts", set, prevOpts); - LeechBlock.setBitPref("prevAddons", set, prevAddons); - LeechBlock.setBitPref("prevConfig", set, prevConfig); - LeechBlock.setBitPref("countFocus", set, countFocus); - LeechBlock.setBitPref("delayFirst", set, delayFirst); - LeechBlock.setCharPref("delaySecs" + set, delaySecs); - LeechBlock.setUniCharPref("blockRE" + set, regexps.block); - LeechBlock.setUniCharPref("allowRE" + set, regexps.allow); - LeechBlock.setUniCharPref("keywordRE" + set, regexps.keyword); - LeechBlock.setCharPref("times" + set, LeechBlock.cleanTimePeriods(times)); - LeechBlock.setCharPref("limitMins" + set, limitMins); - LeechBlock.setCharPref("limitPeriod" + set, limitPeriod); - LeechBlock.setBitPref("conjMode", set, conjMode); - LeechBlock.setIntPref("days" + set, LeechBlock.encodeDays(daySel)); - LeechBlock.setUniCharPref("blockURL" + set, blockURL); - } - - // Set other preferences - let oa = document.getElementById("lb-options-access").value; - let hpp = document.getElementById("lb-options-hpp").checked; - let ham = document.getElementById("lb-options-ham").checked; - let hsm = document.getElementById("lb-options-hsm").checked; - let bep = document.getElementById("lb-options-bep").checked; - let kpb = document.getElementById("lb-options-kpb").checked; - let hcm = document.getElementById("lb-options-hcm").checked; - let htl = document.getElementById("lb-options-htl").checked; - LeechBlock.setIntPref("oa", oa); - LeechBlock.setBoolPref("hpp", hpp); - LeechBlock.setBoolPref("ham", ham); - LeechBlock.setBoolPref("hsm", hsm); - LeechBlock.setCharPref("warnSecs", warnSecs); - LeechBlock.setBoolPref("bep", bep); - LeechBlock.setBoolPref("kpb", kpb); - LeechBlock.setBoolPref("hcm", hcm); - LeechBlock.setBoolPref("htl", htl); - - // Set password - let password = document.getElementById("lb-options-password").value; - LeechBlock.storePassword(password); - - // Save all preferences to file (in case browser not properly closed later on) - LeechBlock.savePreferences(); - - return true; -} - -// Handles options dialog Cancel button -// -LeechBlock.optionsCancel = function () { - return true; -} - -// Disables options for block set -// -LeechBlock.disableSetOptions = function (set) { - let items = [ - "sites", "sites-URL", - "active-block", "count-focus", "delay-first", "delay-secs", - "times", "all-day", - "limit-mins", "limit-period", "mode", - "day0", "day1", "day2", "day3", "day4", "day5", "day6", "every-day", - "block-page", "default-page", "delaying-page", "blank-page", "home-page", - "set-name", "clear-set-name", - "prev-opts", "prev-addons", "prev-config", "cancel-lockdown", - ]; - for (let item of items) { - document.getElementById("lb-" + item + set).disabled = true; - } -} - -// Updates options for password -// -LeechBlock.updatePasswordOptions = function () { - let disabled = document.getElementById("lb-options-access").value != 1; - document.getElementById("lb-options-password").disabled = disabled; - document.getElementById("lb-clear-password").disabled = disabled; - document.getElementById("lb-options-hpp").disabled = disabled; -} - -// Sets outer tab for options -// -LeechBlock.setOuterTab = function (index) { - document.getElementById("lb-options-tabbox").selectedIndex = index; -} - -// Sets inner tab for block set -// -LeechBlock.setInnerTab = function (set, index) { - document.getElementById("lb-tabbox-set" + set).selectedIndex = index; -} - -// Sets time periods to all day -// -LeechBlock.setAllDay = function (set) { - document.getElementById("lb-times" + set).value = LeechBlock.ALL_DAY_TIMES; -} - -// Sets days to every day -// -LeechBlock.setEveryDay = function (set) { - for (let i = 0; i < 7; i++) { - document.getElementById(("lb-day" + i) + set).checked = true; - } -} - -// Sets URL to default page -// -LeechBlock.setDefaultPage = function (set) { - document.getElementById("lb-block-page" + set).value = LeechBlock.DEFAULT_BLOCK_URL; -} - -// Sets URL to delaying page -// -LeechBlock.setDelayingPage = function (set) { - document.getElementById("lb-block-page" + set).value = LeechBlock.DELAYED_BLOCK_URL; -} - -// Sets URL to blank page -// -LeechBlock.setBlankPage = function (set) { - document.getElementById("lb-block-page" + set).value = "about:blank"; -} - -// Sets URL to home page -// -LeechBlock.setHomePage = function (set) { - let prefs = Cc["@mozilla.org/preferences-service;1"] - .getService(Ci.nsIPrefService) - .getBranch("browser.startup."); - // Get all home pages but use only the first - let homepages = prefs.getCharPref("homepage").split("|"); - document.getElementById("lb-block-page" + set).value = homepages[0]; -} - -// Clears custom set name -// -LeechBlock.clearSetName = function (set) { - document.getElementById("lb-set-name" + set).value = ""; -} - -// Clears password for access to options -// -LeechBlock.clearPassword = function () { - document.getElementById("lb-options-password").value = ""; -} - -// Cancels the currently active lockdown -// -LeechBlock.cancelLockdown = function (set) { - // Get confirmation from user - if (!LeechBlock.confirmCancelLockdown()) { - return; - } - - // Reset lockdown component of time data - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[4] = 0; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - - // Disable 'Cancel Lockdown' button - document.getElementById("lb-cancel-lockdown" + set).disabled = true; -} - -// Exports options to a text file -// -LeechBlock.exportOptions = function () { - const nsIFilePicker = Ci.nsIFilePicker; - - // Get user to choose file - let filePicker = Cc["@mozilla.org/filepicker;1"] - .createInstance(nsIFilePicker); - filePicker.init(window, "Export LeechBlock Options", nsIFilePicker.modeSave); - filePicker.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText); - filePicker.filterIndex = 1; - let ret = filePicker.show(); - if (ret != nsIFilePicker.returnOK && ret != nsIFilePicker.returnReplace) { - return; - } - - let text = ""; - - // Add option values for each set - for (let set = 1; set <= 6; set++) { - // Get component values - let setName = document.getElementById("lb-set-name" + set).value; - let sites = document.getElementById("lb-sites" + set).value; - sites = sites.replace(/\s+/g, " ").replace(/(^ +)|( +$)|(\w+:\/+)/g, ""); - let sitesURL = document.getElementById("lb-sites-URL" + set).value; - let activeBlock = document.getElementById("lb-active-block" + set).checked; - let prevOpts = document.getElementById("lb-prev-opts" + set).checked; - let prevAddons = document.getElementById("lb-prev-addons" + set).checked; - let prevConfig = document.getElementById("lb-prev-config" + set).checked; - let countFocus = document.getElementById("lb-count-focus" + set).checked; - let delayFirst = document.getElementById("lb-delay-first" + set).checked; - let delaySecs = document.getElementById("lb-delay-secs" + set).value; - let times = document.getElementById("lb-times" + set).value; - let limitMins = document.getElementById("lb-limit-mins" + set).value; - let limitPeriod = document.getElementById("lb-limit-period" + set).value; - let conjMode = document.getElementById("lb-mode" + set).selectedIndex == 1; - let daySel = new Array(7); - for (let i = 0; i < 7; i++) { - daySel[i] = document.getElementById("lb-day" + i + set).checked; - } - let blockURL = document.getElementById("lb-block-page" + set).value; - - // Add values to text - text += "setName" + set + "=" + setName + "\n"; - text += "sites" + set + "=" + sites + "\n"; - text += "sitesURL" + set + "=" + sitesURL + "\n"; - text += "activeBlock" + set + "=" + activeBlock + "\n"; - text += "prevOpts" + set + "=" + prevOpts + "\n"; - text += "prevAddons" + set + "=" + prevAddons+ "\n"; - text += "prevConfig" + set + "=" + prevConfig + "\n"; - text += "countFocus" + set + "=" + countFocus + "\n"; - text += "delayFirst" + set + "=" + delayFirst + "\n"; - text += "delaySecs" + set + "=" + delaySecs + "\n"; - text += "times" + set + "=" + LeechBlock.cleanTimePeriods(times) + "\n"; - text += "limitMins" + set + "=" + limitMins + "\n"; - text += "limitPeriod" + set + "=" + limitPeriod + "\n"; - text += "conjMode" + set + "=" + conjMode + "\n"; - text += "days" + set + "=" + LeechBlock.encodeDays(daySel) + "\n"; - text += "blockURL" + set + "=" + blockURL + "\n"; - } - - // Add other option values - let oa = document.getElementById("lb-options-access").value; - let password = document.getElementById("lb-options-password").value; - let hpp = document.getElementById("lb-options-hpp").checked; - let ham = document.getElementById("lb-options-ham").checked; - let hsm = document.getElementById("lb-options-hsm").checked; - let warnSecs = document.getElementById("lb-options-warn-secs").value; - let bep = document.getElementById("lb-options-bep").checked; - let kpb = document.getElementById("lb-options-kpb").checked; - let hcm = document.getElementById("lb-options-hcm").checked; - let htl = document.getElementById("lb-options-htl").checked; - text += "oa=" + oa + "\n"; - text += "password=" + password + "\n"; - text += "hpp=" + hpp + "\n"; - text += "ham=" + ham + "\n"; - text += "hsm=" + hsm + "\n"; - text += "warnSecs=" + warnSecs + "\n"; - text += "bep=" + bep + "\n"; - text += "kpb=" + kpb + "\n"; - text += "hcm=" + hcm + "\n"; - text += "htl=" + htl + "\n"; - - // Write text to file - try { - LeechBlock.writeTextFile(filePicker.file, text); - } catch (e) { - console.warn("[LB] Cannot export options to file." - + " [" + filePicker.file.path + "]"); - return; - } -} - -// Imports options from a text file -// -LeechBlock.importOptions = function () { - const nsIFilePicker = Ci.nsIFilePicker; - const replaceChar = Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER; - - let isTrue = function (str) { return /^true$/i.test(str); } - - // Get user to choose file - let filePicker = Cc["@mozilla.org/filepicker;1"] - .createInstance(nsIFilePicker); - filePicker.init(window, "Import LeechBlock Options", nsIFilePicker.modeOpen); - filePicker.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText); - filePicker.filterIndex = 1; - let ret = filePicker.show(); - if (ret != nsIFilePicker.returnOK) { - return; - } - - let text = ""; - - // Read text from file - try { - text = LeechBlock.readTextFile(filePicker.file); - } catch (e) { - console.warn("[LB] Cannot import options from file." - + " [" + filePicker.file.path + "]"); - } - - // Get options from text - let regexp = /^(\w+)=(.*)$/; - let lines = text.split(/\n/); - let opts = {}; - for (let line of lines) { - let results = regexp.exec(line); - if (results != null) { - opts[results[1]] = results[2]; - } - } - - // Process option values for each set - for (let set = 1; set <= 6; set++) { - // Get values from options - let setName = opts["setName" + set]; - let sites = opts["sites" + set]; - let sitesURL = opts["sitesURL" + set]; - let activeBlock = opts["activeBlock" + set]; - let prevOpts = opts["prevOpts" + set]; - let prevAddons = opts["prevAddons" + set]; - let prevConfig = opts["prevConfig" + set]; - let countFocus = opts["countFocus" + set]; - let delayFirst = opts["delayFirst" + set]; - let delaySecs = opts["delaySecs" + set]; - let times = opts["times" + set]; - let limitMins = opts["limitMins" + set] - let limitPeriod = opts["limitPeriod" + set] - let conjMode = opts["conjMode" + set]; - let days = opts["days" + set]; - let blockURL = opts["blockURL" + set]; - - // Set component values - if (setName != undefined) { - let element = document.getElementById("lb-set-name" + set); - if (!element.disabled) { - element.value = setName; - } - } - if (sites != undefined) { - sites = sites.replace(/\s+/g, "\n"); - let element = document.getElementById("lb-sites" + set); - if (!element.disabled) { - element.value = sites; - } - } - if (sitesURL != undefined) { - let element = document.getElementById("lb-sites-URL" + set); - if (!element.disabled) { - element.value = sitesURL; - } - } - if (activeBlock != undefined) { - let element = document.getElementById("lb-active-block" + set); - if (!element.disabled) { - element.checked = isTrue(activeBlock); - } - } - if (prevOpts != undefined) { - let element = document.getElementById("lb-prev-opts" + set); - if (!element.disabled) { - element.checked = isTrue(prevOpts); - } - } - if (prevAddons != undefined) { - let element = document.getElementById("lb-prev-addons" + set); - if (!element.disabled) { - element.checked = isTrue(prevAddons); - } - } - if (prevConfig != undefined) { - let element = document.getElementById("lb-prev-config" + set); - if (!element.disabled) { - element.checked = isTrue(prevConfig); - } - } - if (countFocus != undefined) { - let element = document.getElementById("lb-count-focus" + set); - if (!element.disabled) { - element.checked = isTrue(countFocus); - } - } - if (delayFirst != undefined) { - let element = document.getElementById("lb-delay-first" + set); - if (!element.disabled) { - element.checked = isTrue(delayFirst); - } - } - if (delaySecs != undefined) { - let element = document.getElementById("lb-delay-secs" + set); - if (!element.disabled) { - element.value = delaySecs; - } - } - if (times != undefined) { - let element = document.getElementById("lb-times" + set); - if (!element.disabled) { - element.value = times; - } - } - if (limitMins != undefined) { - let element = document.getElementById("lb-limit-mins" + set); - if (!element.disabled) { - element.value = limitMins; - } - } - if (limitPeriod != undefined) { - let element = document.getElementById("lb-limit-period" + set); - if (!element.disabled) { - element.value = limitPeriod; - } - } - if (conjMode != undefined) { - let element = document.getElementById("lb-mode" + set); - if (!element.disabled) { - element.selectedIndex = isTrue(conjMode) ? 1 : 0; - } - } - if (days != undefined) { - let daySel = LeechBlock.decodeDays(days); - for (let i = 0; i < 7; i++) { - let element = document.getElementById("lb-day" + i + set); - if (!element.disabled) { - element.checked = daySel[i]; - } - } - } - if (blockURL != undefined) { - let element = document.getElementById("lb-block-page" + set); - if (!element.disabled) { - element.value = blockURL; - } - } - } - - // Process other option values - let oa = opts["oa"]; - let password = opts["password"]; - let hpp = opts["hpp"]; - let ham = opts["ham"]; - let hsm = opts["hsm"]; - let warnSecs = opts["warnSecs"]; - let bep = opts["bep"]; - let kpb = opts["kpb"]; - let hcm = opts["hcm"]; - let htl = opts["htl"]; - if (oa != undefined) { - document.getElementById("lb-options-access").value = oa; - } - if (password != undefined) { - document.getElementById("lb-options-password").value = password; - } - if (hpp != undefined) { - document.getElementById("lb-options-hpp").checked = isTrue(hpp); - } - if (ham != undefined) { - document.getElementById("lb-options-ham").checked = isTrue(ham); - } - if (hsm != undefined) { - document.getElementById("lb-options-hsm").checked = isTrue(hsm); - } - if (warnSecs != undefined) { - document.getElementById("lb-options-warn-secs").value = warnSecs; - } - if (bep != undefined) { - document.getElementById("lb-options-bep").checked = isTrue(bep); - } - if (kpb != undefined) { - document.getElementById("lb-options-kpb").checked = isTrue(kpb); - } - if (hcm != undefined) { - document.getElementById("lb-options-hcm").checked = isTrue(hcm); - } - if (htl != undefined) { - document.getElementById("lb-options-htl").checked = isTrue(htl); - } - LeechBlock.updatePasswordOptions(); -} - -// Shows alert dialog for bad time periods entry -// -LeechBlock.alertBadTimes = function () { - LeechBlock.PROMPTS.alert(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_alertBadTimes); -} - -// Shows alert dialog for bad time limit entry -// -LeechBlock.alertBadTimeLimit = function () { - LeechBlock.PROMPTS.alert(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_alertBadTimeLimit); -} - -// Shows alert dialog for bad seconds entry -// -LeechBlock.alertBadSeconds = function () { - LeechBlock.PROMPTS.alert(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_alertBadSeconds); -} - -// Shows confirmation dialog for preventing access to options all day -// -LeechBlock.confirmPrevOptsAllDay = function () { - let check = {value: false}; - let flags = - LeechBlock.PROMPTS.BUTTON_POS_0 * LeechBlock.PROMPTS.BUTTON_TITLE_YES + - LeechBlock.PROMPTS.BUTTON_POS_1 * LeechBlock.PROMPTS.BUTTON_TITLE_NO; - let button = LeechBlock.PROMPTS.confirmEx(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_confPrevOptsAllDay, - flags, "", "", "", null, check); - return (button == 0); -} - -// Shows confirmation dialog for canceling lockdown -// -LeechBlock.confirmCancelLockdown = function () { - let check = {value: false}; - let flags = - LeechBlock.PROMPTS.BUTTON_POS_0 * LeechBlock.PROMPTS.BUTTON_TITLE_YES + - LeechBlock.PROMPTS.BUTTON_POS_1 * LeechBlock.PROMPTS.BUTTON_TITLE_NO; - let button = LeechBlock.PROMPTS.confirmEx(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_confCancelLockdown, - flags, "", "", "", null, check); - return (button == 0); -} - -// Shows prompt dialog for password -// -LeechBlock.requestPassword = function (hide) { - let userpassword = {}, check = {}; - if (hide) { - LeechBlock.PROMPTS.promptPassword(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_passwordPrompt, - userpassword, null, check); - } else { - LeechBlock.PROMPTS.prompt(null, - LeechBlock.locale_options_title, - LeechBlock.locale_options_passwordPrompt, - userpassword, null, check); - } - return userpassword.value; -} diff --git a/chrome/content/options.xul b/chrome/content/options.xul deleted file mode 100644 index 92256cf..0000000 --- a/chrome/content/options.xul +++ /dev/null @@ -1,1497 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - &options.whatBlockDesc; - - - &options.customSetName; - - diff --git a/chrome/content/stats.js b/chrome/content/stats.js deleted file mode 100644 index cb1356c..0000000 --- a/chrome/content/stats.js +++ /dev/null @@ -1,107 +0,0 @@ -/* 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/. */ - -/* - * This file contains the code for the Statistics dialog. - */ - -// Refreshes values in statistics dialog -// -LeechBlock.statsRefresh = function () { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - for (let set = 1; set <= 6; set++) { - // Get preferences for this set - let setName = LeechBlock.getUniCharPref("setName" + set); - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let limitMins = LeechBlock.getCharPref("limitMins" + set); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let periodStart = LeechBlock.getTimePeriodStart(now, limitPeriod); - - // Update block set name - if (setName == "") { - setName = LeechBlock.locale_blockSet + " " + set; - } - document.getElementById("lb-set-name" + set).value = setName; - - // Update time values - if (timedata.length == 5) { - let fs = LeechBlock.getFormattedStats(timedata); - document.getElementById("lb-start-time" + set).value = fs.startTime; - document.getElementById("lb-total-time" + set).value = fs.totalTime; - document.getElementById("lb-per-day-time" + set).value = fs.perDayTime; - - if (limitMins != "" && limitPeriod != "") { - // Calculate total seconds left in this time period - let secsLeft = timedata[2] == periodStart - ? Math.max(0, (limitMins * 60) - timedata[3]) - : (limitMins * 60); - let timeLeft = LeechBlock.formatTime(secsLeft); - document.getElementById("lb-time-left" + set).value = timeLeft; - } - } - } -} - -// Restarts data gathering for block set -// -LeechBlock.statsRestart = function (set) { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - // Update time data for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[0] = now; - timedata[1] = 0; - } else { - timedata = [now, 0, 0, 0, 0]; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - - // Update display for this set - let fs = LeechBlock.getFormattedStats(timedata); - document.getElementById("lb-start-time" + set).value = fs.startTime; - document.getElementById("lb-total-time" + set).value = fs.totalTime; - document.getElementById("lb-per-day-time" + set).value = fs.perDayTime; -} - -// Restarts data gathering for all block sets -// -LeechBlock.statsRestartAll = function () { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - for (let set = 1; set <= 6; set++) { - // Update time data for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[0] = now; - timedata[1] = 0; - } else { - timedata = [now, 0, 0, 0, 0]; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - - // Update display for this set - let fs = LeechBlock.getFormattedStats(timedata); - document.getElementById("lb-start-time" + set).value = fs.startTime; - document.getElementById("lb-total-time" + set).value = fs.totalTime; - document.getElementById("lb-per-day-time" + set).value = fs.perDayTime; - } -} - -// Returns formatted times based on time data -// -LeechBlock.getFormattedStats = function (timedata) { - let days = 1 - + Math.floor(Date.now() / 86400000) - - Math.floor(timedata[0] / 86400); - return { - startTime: new Date(timedata[0] * 1000).toLocaleString(), - totalTime: LeechBlock.formatTime(timedata[1]), - perDayTime: LeechBlock.formatTime(timedata[1] / days) - }; -} diff --git a/chrome/content/stats.xul b/chrome/content/stats.xul deleted file mode 100644 index 860fd80..0000000 --- a/chrome/content/stats.xul +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/style.css b/chrome/content/style.css deleted file mode 100644 index 2f7b0e2..0000000 --- a/chrome/content/style.css +++ /dev/null @@ -1,41 +0,0 @@ -/* LeechBlock CSS style */ - -body { - background-color: #fff; - color: #2e3436; - font: normal 16px "Open Sans", Arial, sans-serif; - text-align: center; -} - -div { - margin-top: 50px; -} - -h1 { - font-size: 24px; - font-weight: 300; -} - -a { - color: #2ca089; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -.freedom { - font-size: 14px; -} - -.support { - color: #777; - font-size: 14px; - font-style: italic; -} - -.support a { - color: #777; - text-decoration: underline; -} diff --git a/chrome/locale/cs-CZ/leechblock.dtd b/chrome/locale/cs-CZ/leechblock.dtd deleted file mode 100644 index ed489f9..0000000 --- a/chrome/locale/cs-CZ/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/cs-CZ/leechblock.properties b/chrome/locale/cs-CZ/leechblock.properties deleted file mode 100644 index a7c6501..0000000 --- a/chrome/locale/cs-CZ/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Zablokuje webové stránky, které zbytečně odvádějí vaši pozornost od práce, když se to nejméně hodí. diff --git a/chrome/locale/en-US/leechblock.dtd b/chrome/locale/en-US/leechblock.dtd deleted file mode 100644 index 7f69ab2..0000000 --- a/chrome/locale/en-US/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/en-US/leechblock.properties b/chrome/locale/en-US/leechblock.properties deleted file mode 100644 index 1aa614a..0000000 --- a/chrome/locale/en-US/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Blocks those time-wasting sites that can suck the life out of your working day. diff --git a/chrome/locale/fr-FR/leechblock.dtd b/chrome/locale/fr-FR/leechblock.dtd deleted file mode 100644 index 5c13ca0..0000000 --- a/chrome/locale/fr-FR/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/fr-FR/leechblock.properties b/chrome/locale/fr-FR/leechblock.properties deleted file mode 100644 index 3fbe3e0..0000000 --- a/chrome/locale/fr-FR/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Bloque tous ces sites chronophages qui ruinent votre productivité. diff --git a/chrome/locale/pt-BR/leechblock.dtd b/chrome/locale/pt-BR/leechblock.dtd deleted file mode 100644 index 53950dc..0000000 --- a/chrome/locale/pt-BR/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/pt-BR/leechblock.properties b/chrome/locale/pt-BR/leechblock.properties deleted file mode 100644 index 3d82d79..0000000 --- a/chrome/locale/pt-BR/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Bloqueia aqueles sites que fazem você gastar o tempo útil do seu dia. diff --git a/chrome/locale/zh-CN/leechblock.dtd b/chrome/locale/zh-CN/leechblock.dtd deleted file mode 100644 index 211d0e9..0000000 --- a/chrome/locale/zh-CN/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/zh-CN/leechblock.properties b/chrome/locale/zh-CN/leechblock.properties deleted file mode 100644 index 8f9b790..0000000 --- a/chrome/locale/zh-CN/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=阻止那些浪费时间的网站在你的工作日。 diff --git a/chrome/locale/zh-TW/leechblock.dtd b/chrome/locale/zh-TW/leechblock.dtd deleted file mode 100644 index 02f3177..0000000 --- a/chrome/locale/zh-TW/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/zh-TW/leechblock.properties b/chrome/locale/zh-TW/leechblock.properties deleted file mode 100644 index ba9a96d..0000000 --- a/chrome/locale/zh-TW/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=封鎖浪費工作日生命的網站。 diff --git a/chrome/skin/toolbar.css b/chrome/skin/toolbar.css deleted file mode 100644 index 3933e23..0000000 --- a/chrome/skin/toolbar.css +++ /dev/null @@ -1,17 +0,0 @@ -#leechblock-toolbar-button { - list-style-image: url("chrome://leechblock/skin/leechblock24.png"); -} - -toolbar[iconsize="small"] #leechblock-toolbar-button { - list-style-image: url("chrome://leechblock/skin/leechblock16.png"); -} - -#leechblock-time-left { - background-color: #bbb; - border: 1px solid #808080; - border-radius: 4px; - color: #444; - font: normal 12px "Lucida Console", "Monaco", monospace; - margin: 0px; - padding: 4px 4px 2px 4px; -} diff --git a/defaults/preferences/leechblock.js b/defaults/preferences/leechblock.js deleted file mode 100644 index f41d8c8..0000000 --- a/defaults/preferences/leechblock.js +++ /dev/null @@ -1,188 +0,0 @@ -pref("extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description", "chrome://leechblock/locale/leechblock.properties"); -pref("extensions.leechblock.setName1", ""); -pref("extensions.leechblock.setName2", ""); -pref("extensions.leechblock.setName3", ""); -pref("extensions.leechblock.setName4", ""); -pref("extensions.leechblock.setName5", ""); -pref("extensions.leechblock.setName6", ""); -pref("extensions.leechblock.sites1", ""); -pref("extensions.leechblock.sites2", ""); -pref("extensions.leechblock.sites3", ""); -pref("extensions.leechblock.sites4", ""); -pref("extensions.leechblock.sites5", ""); -pref("extensions.leechblock.sites6", ""); -pref("extensions.leechblock.sitesURL1", ""); -pref("extensions.leechblock.sitesURL2", ""); -pref("extensions.leechblock.sitesURL3", ""); -pref("extensions.leechblock.sitesURL4", ""); -pref("extensions.leechblock.sitesURL5", ""); -pref("extensions.leechblock.sitesURL6", ""); -pref("extensions.leechblock.blockRE1", ""); -pref("extensions.leechblock.blockRE2", ""); -pref("extensions.leechblock.blockRE3", ""); -pref("extensions.leechblock.blockRE4", ""); -pref("extensions.leechblock.blockRE5", ""); -pref("extensions.leechblock.blockRE6", ""); -pref("extensions.leechblock.allowRE1", ""); -pref("extensions.leechblock.allowRE2", ""); -pref("extensions.leechblock.allowRE3", ""); -pref("extensions.leechblock.allowRE4", ""); -pref("extensions.leechblock.allowRE5", ""); -pref("extensions.leechblock.allowRE6", ""); -pref("extensions.leechblock.keywordRE1", ""); -pref("extensions.leechblock.keywordRE2", ""); -pref("extensions.leechblock.keywordRE3", ""); -pref("extensions.leechblock.keywordRE4", ""); -pref("extensions.leechblock.keywordRE5", ""); -pref("extensions.leechblock.keywordRE6", ""); -pref("extensions.leechblock.times1", "0900-1700"); -pref("extensions.leechblock.times2", "0900-1700"); -pref("extensions.leechblock.times3", "0900-1700"); -pref("extensions.leechblock.times4", "0900-1700"); -pref("extensions.leechblock.times5", "0900-1700"); -pref("extensions.leechblock.times6", "0900-1700"); -pref("extensions.leechblock.limitMins1", ""); -pref("extensions.leechblock.limitMins2", ""); -pref("extensions.leechblock.limitMins3", ""); -pref("extensions.leechblock.limitMins4", ""); -pref("extensions.leechblock.limitMins5", ""); -pref("extensions.leechblock.limitMins6", ""); -pref("extensions.leechblock.limitPeriod1", ""); -pref("extensions.leechblock.limitPeriod2", ""); -pref("extensions.leechblock.limitPeriod3", ""); -pref("extensions.leechblock.limitPeriod4", ""); -pref("extensions.leechblock.limitPeriod5", ""); -pref("extensions.leechblock.limitPeriod6", ""); -pref("extensions.leechblock.conjMode", 0); -pref("extensions.leechblock.days1", 62); -pref("extensions.leechblock.days2", 62); -pref("extensions.leechblock.days3", 62); -pref("extensions.leechblock.days4", 62); -pref("extensions.leechblock.days5", 62); -pref("extensions.leechblock.days6", 62); -pref("extensions.leechblock.blockURL1", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL2", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL3", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL4", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL5", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL6", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.activeBlock", 0); -pref("extensions.leechblock.countFocus", 63); -pref("extensions.leechblock.delayFirst", 63); -pref("extensions.leechblock.delaySecs1", "60"); -pref("extensions.leechblock.delaySecs2", "60"); -pref("extensions.leechblock.delaySecs3", "60"); -pref("extensions.leechblock.delaySecs4", "60"); -pref("extensions.leechblock.delaySecs5", "60"); -pref("extensions.leechblock.delaySecs6", "60"); -pref("extensions.leechblock.prevOpts", 0); -pref("extensions.leechblock.prevAddons", 0); -pref("extensions.leechblock.prevConfig", 0); -pref("extensions.leechblock.oa", 0); -pref("extensions.leechblock.hpp", true); -pref("extensions.leechblock.ham", false); -pref("extensions.leechblock.hsm", false); -pref("extensions.leechblock.warnSecs", ""); -pref("extensions.leechblock.bep", false); -pref("extensions.leechblock.kpb", false); -pref("extensions.leechblock.hcm", false); -pref("extensions.leechblock.htl", false); -pref("extensions.leechblock.timedata1", ""); -pref("extensions.leechblock.timedata2", ""); -pref("extensions.leechblock.timedata3", ""); -pref("extensions.leechblock.timedata4", ""); -pref("extensions.leechblock.timedata5", ""); -pref("extensions.leechblock.timedata6", ""); -pref("extensions.leechblock.repeatCheckPeriod", 5000); -pref("extensions.leechblock.lockdownDuration", 0); -pref("extensions.leechblock.lockdownSets", 0); -pref("extensions.leechblock.ao", ""); -pref("extensions.leechblock.ap", ""); -pref("services.sync.prefs.sync.extensions.leechblock.setName1", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName2", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName3", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName4", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName5", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName6", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites1", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites2", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites3", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites4", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites5", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites6", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL1", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL2", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL3", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL4", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL5", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL6", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE1", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE2", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE3", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE4", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE5", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE6", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE1", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE2", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE3", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE4", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE5", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE6", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE1", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE2", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE3", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE4", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE5", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE6", true); -pref("services.sync.prefs.sync.extensions.leechblock.times1", true); -pref("services.sync.prefs.sync.extensions.leechblock.times2", true); -pref("services.sync.prefs.sync.extensions.leechblock.times3", true); -pref("services.sync.prefs.sync.extensions.leechblock.times4", true); -pref("services.sync.prefs.sync.extensions.leechblock.times5", true); -pref("services.sync.prefs.sync.extensions.leechblock.times6", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins1", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins2", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins3", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins4", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins5", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins6", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod1", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod2", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod3", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod4", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod5", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod6", true); -pref("services.sync.prefs.sync.extensions.leechblock.conjMode", true); -pref("services.sync.prefs.sync.extensions.leechblock.days1", true); -pref("services.sync.prefs.sync.extensions.leechblock.days2", true); -pref("services.sync.prefs.sync.extensions.leechblock.days3", true); -pref("services.sync.prefs.sync.extensions.leechblock.days4", true); -pref("services.sync.prefs.sync.extensions.leechblock.days5", true); -pref("services.sync.prefs.sync.extensions.leechblock.days6", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL1", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL2", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL3", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL4", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL5", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL6", true); -pref("services.sync.prefs.sync.extensions.leechblock.activeBlock", true); -pref("services.sync.prefs.sync.extensions.leechblock.countFocus", true); -pref("services.sync.prefs.sync.extensions.leechblock.delayFirst", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs1", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs2", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs3", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs4", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs5", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs6", true); -pref("services.sync.prefs.sync.extensions.leechblock.prevOpts", true); -pref("services.sync.prefs.sync.extensions.leechblock.prevAddons", true); -pref("services.sync.prefs.sync.extensions.leechblock.prevConfig", true); -pref("services.sync.prefs.sync.extensions.leechblock.oa", true); -pref("services.sync.prefs.sync.extensions.leechblock.hpp", true); -pref("services.sync.prefs.sync.extensions.leechblock.ham", true); -pref("services.sync.prefs.sync.extensions.leechblock.hsm", true); -pref("services.sync.prefs.sync.extensions.leechblock.warnSecs", true); -pref("services.sync.prefs.sync.extensions.leechblock.bep", true); -pref("services.sync.prefs.sync.extensions.leechblock.kpb", true); -pref("services.sync.prefs.sync.extensions.leechblock.hcm", true); -pref("services.sync.prefs.sync.extensions.leechblock.htl", true); diff --git a/install.rdf b/install.rdf deleted file mode 100644 index c225adb..0000000 --- a/install.rdf +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {a95d8332-e4b4-6e7f-98ac-20b733364387} - 1.0.8 - 2 - true - LeechBlock - Blocks those time-wasting sites that can suck the life out of your working day. - James Anderson - Michal Stanke (cs-CZ) - ioreker (fr-FR) - Guillaume Bignon (fr-FR) - Gutierrez PS (pt-BR) [http://about.me/gutierrezps] - Wang.H.K (zh-CN) - Rex Tsai (zh-TW) - Allen Chen (zh-CN & zh-TW) - Dario Kolak - chrome://leechblock/content/options.xul - chrome://leechblock/skin/leechblock32.png - chrome://leechblock/skin/leechblock64.png - http://www.proginosko.com/leechblock/ - - - {ec8030f7-c20a-464f-9b0e-13a3a9e97384} - 38.0 - 54.* - - - - diff --git a/lib/logic.js b/lib/logic.js new file mode 100644 index 0000000..11b50c4 --- /dev/null +++ b/lib/logic.js @@ -0,0 +1,211 @@ +'use strict' + +const {matchPatternToRegExp} = require('./match-pattern') +const moment = require('moment'); +const {parseISOTime, parseISODateTime, formatISODateTime} = require('./state') + +/** + * @typedef DailyInterval + * @type {object} + * @property {DayTime} startTime + * @property {DayTime} endTime + * @property {Array.} days + */ + +/** + * Whether a moment is inside a daily interval. + * @param {moment} + * @param {DailyInterval} + * @return {boolean} + */ +function isInsideDailyInterval(m, {startTime, endTime, days}) { + if (!days[m.day()]) { + return false + } + + const startm = m.clone().set(startTime); + const endm = m.clone().set(endTime); + + return startm <= m && m < endm; +} + +/** + * @param {moment} + * @param {DailyInterval} + * @return {moment} + */ +function nextMomentInsideDailyInterval(m, dailyInterval) { + if (isInsideDailyInterval(m, dailyInterval)) { + return m.clone() + } + const {startTime, endTime, days} = dailyInterval; + + if ( + moment.duration(endTime).valueOf() === moment.duration(startTime).valueOf() + ) { + return undefined; + } + + if (days[m.day()]) { + const startToday = m.clone().set(startTime) + if (m <= startToday) { + return startToday + } + } + + for (const i of [1, 2, 3, 4, 5, 6, 7]) { + const n = m.clone().add(i, 'days') + if (days[n.day()]) { + return n.set(startTime) + } + } + + return undefined +} + +/** + * @param {moment} + * @param {DailyInterval} + * @return {moment} + */ +function nextMomentOutsideDailyInterval(m, dailyInterval) { + if (!isInsideDailyInterval(m, dailyInterval)) { + return m.clone(); + } + const {startTime, endTime, days} = dailyInterval; + + if ( + moment.duration(startTime).valueOf() === 0 && + moment.duration(endTime).valueOf() === moment.duration(24, 'hours').valueOf() + ) { + for (const i of [1, 2, 3, 4, 5, 6]) { + const n = m.clone().add(i, 'days') + if (!days[n.day()]) { + return n.set(startTime) + } + } + return undefined + } + + return m.clone().set(endTime) +} + +function nextIntervalBoundary(now, start, duration) { + const result = start.clone() + if (start <= now) { + while (result < now) { + result.add(duration) + } + } else { + while (result > now) { + result.subtract(duration) + } + result.add(duration) + } + return result +} + +function previousIntervalBoundary(now, start, duration) { + return nextIntervalBoundary(now.clone().subtract(duration), start, duration) +} + +function affectsUrl(url, {blockedUrlPatterns}, {taintedUrls}) { + const patterns = blockedUrlPatterns.concat(taintedUrls) + + return patterns.some(pattern => matchPatternToRegExp(pattern).test(url)) +} + +function currentStatus( + now, + {startTime, endTime, days, quotaInterval, quotaAllowed}, + {quotaUsed, lastQuotaReset} +) { + startTime = parseISOTime(startTime) + endTime = parseISOTime(endTime) + quotaInterval = moment.duration(quotaInterval) + quotaAllowed = moment.duration(quotaAllowed) + + quotaUsed = moment.duration(quotaUsed) + lastQuotaReset = parseISODateTime(lastQuotaReset) + + if (!isInsideDailyInterval(now, {startTime, endTime, days})) { + return "inactive" + } + + const nextQuotaReset = nextIntervalBoundary(now, lastQuotaReset, quotaInterval) + if (nextQuotaReset <= now && quotaAllowed > moment.duration(0)) { + return "tracking" + } + + if (nextQuotaReset > now && quotaAllowed > quotaUsed) { + return "tracking" + } + + return "blocking" +} + +function nextNonBlockingMoment(now, settings, data) { + if (currentStatus(now, settings, data) !== "blocking") { + return now; + } + + const startTime = parseISOTime(settings.startTime) + const endTime = parseISOTime(settings.endTime) + const nextInactive = + nextMomentOutsideDailyInterval(now, {startTime, endTime, days: settings.days}) + + const quotaAllowed = moment.duration(settings.quotaAllowed) + const quotaReset = parseISODateTime(settings.quotaReset) + const quotaInterval = moment.duration(settings.quotaInterval) + + let nextReset = undefined + if (quotaAllowed > moment.duration(0)) { + nextReset = nextIntervalBoundary(now, quotaReset, quotaInterval) + } + + if (nextInactive == undefined) { + return nextReset; + } + if (nextReset == undefined) { + return nextInactive; + } + return moment.min([nextInactive, nextReset]); +} + +function quotaTick(getActiveUrl, getAllUrls, now, settings, data) { + // reset the quota usage if next reset moment is not in the future anymore + const quotaReset = parseISODateTime(settings.quotaReset) + const lastQuotaReset = parseISODateTime(data.lastQuotaReset) + const quotaInterval = moment.duration(settings.quotaInterval) + const lastQuotaReset_ = previousIntervalBoundary(now, quotaReset, quotaInterval) + + if (lastQuotaReset.valueOf() !== lastQuotaReset_.valueOf()) { + data.lastQuotaReset = formatISODateTime(lastQuotaReset_) + data.quotaUsed = String(moment.duration(0)) + settings.quotaReset = lastQuotaReset_ + } + + // increase quotaUsed if the blockset is currenlty in tracking status + if (currentStatus(now, settings, data) === "tracking") { + return getActiveUrl().then(url => { + if (url != undefined && affectsUrl(url, settings, data)) { + data.quotaUsed = String(moment.duration(data.quotaUsed).add(1, 'seconds')) + } + return data + }) + } + + return Promise.resolve(); +} + +module.exports = { + isInsideDailyInterval, + nextMomentInsideDailyInterval, + nextMomentOutsideDailyInterval, + nextIntervalBoundary, + previousIntervalBoundary, + affectsUrl, + currentStatus, + nextNonBlockingMoment, + quotaTick +} diff --git a/lib/match-pattern.js b/lib/match-pattern.js new file mode 100644 index 0000000..d4b2d74 --- /dev/null +++ b/lib/match-pattern.js @@ -0,0 +1,35 @@ +'use strict' + +// matches all valid match patterns (except '') +// and extracts [ , scheme, host, path, ] +const matchPattern = (/^(?:(\*|http|https|file|ftp|app):\/\/([^\/]+|)\/?(.*))$/i); + +/** + * Transforms a valid match pattern into a regular expression + * which matches all URLs included by that pattern. + * + * @param {string} pattern The pattern to transform. + * @return {RegExp} The pattern's equivalent as a RegExp. + * @throws {TypeError} If the pattern is not a valid MatchPattern + */ +function matchPatternToRegExp(pattern) { + if (pattern === '') { + return (/^(?:https?|file|ftp|app):\/\//); + } + const match = matchPattern.exec(pattern); + if (!match) { + return undefined + } + const [ , scheme, host, path, ] = match; + return new RegExp('^(?:' + + (scheme === '*' ? 'https?' : escape(scheme)) + ':\\/\\/' + + (host === '*' ? "[^\\/]*" : escape(host).replace(/^\*\./g, '(?:[^\\/]+)?')) + + (path ? (path == '*' ? '(?:\\/.*)?' : ('\\/' + escape(path).replace(/\*/g, '.*'))) : '\\/?') + + ')$'); +} + +if (typeof module !== 'undefined') { + module.exports = { + matchPatternToRegExp + } +} diff --git a/lib/state.js b/lib/state.js new file mode 100644 index 0000000..da40452 --- /dev/null +++ b/lib/state.js @@ -0,0 +1,184 @@ +'use strict' + +// Utilities for handling persistent state +const moment = require('moment') + +// parse and format day time in format HH:mm:ss +function parseISOTime(str) { + if (str === "24:00:00") { + // moment would say that this is 00:00:00 on the next day + return { + hours: 24, + minutes: 0, + seconds: 0, + milliseconds: 0 + } + } else { + const m = moment(str, "HH:mm:ss") + return { + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: 0 + } + } +} +function formatISOTime(t) { + if (t.hours === 24) { + return "24:00:00" + } else { + return moment(t).format("HH:mm:ss") + } +} +// parse and format an ISO date and time without time zone into a moment (local time) +function parseISODateTime(str) { + return moment(str) +} +function formatISODateTime(m) { + // TODO: make more robust + return m.format().slice(0, -6) +} + +/** + * @typedef BlockSetSettings + * @type {object} + * @property {string} id - an UUID4 string + * @property {string} name - any string + * + * @property {Array.} blockedUrlPatterns - valid match patterns only + * + * @property {string} startTime - format HH:mm:ss + * @property {string} endTime - format HH:mm:ss + * @property {Array.} days - always of length 7 + * + * @property {string} quotaInterval - an ISO 8601 duration + * @property {string} quotaReset - an ISO 8601 date and time, without time zone + * @property {string} quotaAllowed - an ISO 8601 duration + */ + +const exampleBlockSetSettings = { + id: "cee7cf25-6513-4281-9e32-9ed464d96614", + name: "My block set", + + blockedUrlPatterns: ["*://does-not-exist.com/*", "*://www.heise.de/*"], + + startTime: "00:00:00", + endTime: "24:00:00", + days: [true, true, true, true, true, true, true], + + quotaInterval: "P1Y", + quotaReset: "2017-09-14T10:27:22", + quotaAllowed: "PT1H30M" +} + +// courtesy https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript +function uuidv4() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +function emptyBlockSet() { + return { + id: uuidv4(), + name: "", + + blockedUrlPatterns: [], + startTime: "09:00:00", + endTime: "17:00:00", + days: [false, true, true, true, true, true, false], + + quotaInterval: "P1D", + quotaReset: "2017-09-14T00:00:00", + quotaAllowed: "PT10M" + } +} + +/** + * @typedef BlockSetData + * @property {string} quotaUsed - an ISO 8601 duration + * @property {string} lastQuotaReset - an ISO 8601 date and time, without time zone + * @property {Array.} taintedUrls + */ + +const exampleBlockSetData = { + quotaUsed: "PT1H29M58S", + lastQuotaReset: "2017-09-14T10:27:22", + taintedUrls: ["http://visited-from-does-not-exist.com"] +} + +/** + * @typedef State + * @property {Array.} blockSets + * @property {object} blockSetData - maps ids to BlockSetData`s + */ + +const exampleState = { + blockSetSettings: [exampleBlockSetSettings], + blockSetData: {} +} +exampleState.blockSetData[exampleBlockSetSettings.id] = exampleBlockSetData + +function defaultState() { + return { + blockSetSettings: [emptyBlockSet()], + blockSetData: {} + } +} + +function setState(state) { + return browser.storage.local.set(state) +} + +function insertMissingBlockSetData(state) { + for (const settings of state.blockSetSettings) { + if (!(settings.id in state.blockSetData)) { + state.blockSetData[settings.id] = { + lastQuotaReset: settings.quotaReset, + quotaUsed: String(moment.duration(0)), + taintedUrls: [] + } + } + } +} + +function getState() { + const keys = ["blockSetSettings", "blockSetData"] + return browser.storage.local.get(keys).then( + state => { + if (keys.some(key => !(key in state))) { + return defaultState() + } else { + return state + } + }, + err => { + return defaultState() + } + ).then(state => { + insertMissingBlockSetData(state) + return state + }) +} + +function syncState(state) { + browser.storage.onChanged.addListener(changes => { + for (const key in changes) { + state[key] = changes[key].newValue + } + insertMissingBlockSetData(state) + }); +} + +module.exports = { + parseISOTime, + formatISOTime, + parseISODateTime, + formatISODateTime, + getState, + setState, + exampleState, + defaultState, + syncState +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..7153991 --- /dev/null +++ b/manifest.json @@ -0,0 +1,30 @@ +{ + "manifest_version": 2, + "name": "LeechBlock", + "version": "2.0.0", + + "description": "Blocks those time-wasting sites that can suck the life out of your working day.", + + "icons": { + "32": "assets/leechblock32.png", + "64": "assets/leechblock64.png" + }, + + "background": { + "scripts": [ + "background.js" + ] + }, + + "options_ui": { + "page": "settings/en_US.html", + "open_in_tab": true + }, + + "web_accessible_resources": [ + "block/*", + "assets/*" + ], + + "permissions": ["storage", "tabs"] +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..69bcdc6 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1355 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, + "acorn": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", + "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" + }, + "array-filter": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", + "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=" + }, + "array-map": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz", + "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=" + }, + "array-reduce": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", + "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=" + }, + "asn1.js": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", + "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=", + "requires": { + "bn.js": "4.11.8", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "assert": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", + "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", + "requires": { + "util": "0.10.3" + } + }, + "astw": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz", + "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=", + "requires": { + "acorn": "4.0.13" + } + }, + "babylon": { + "version": "7.0.0-beta.19", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", + "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base64-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", + "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==" + }, + "bluebird": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", + "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browser-pack": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz", + "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", + "requires": { + "JSONStream": "1.3.1", + "combine-source-map": "0.7.2", + "defined": "1.0.0", + "through2": "2.0.3", + "umd": "3.0.1" + } + }, + "browser-resolve": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz", + "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=", + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" + } + } + }, + "browser-stdout": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", + "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=" + }, + "browserify": { + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/browserify/-/browserify-14.4.0.tgz", + "integrity": "sha1-CJo0Y69Y0OSNjNQHCz90ZU1avKk=", + "requires": { + "JSONStream": "1.3.1", + "assert": "1.4.1", + "browser-pack": "6.0.2", + "browser-resolve": "1.11.2", + "browserify-zlib": "0.1.4", + "buffer": "5.0.7", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "console-browserify": "1.1.0", + "constants-browserify": "1.0.0", + "crypto-browserify": "3.11.1", + "defined": "1.0.0", + "deps-sort": "2.0.0", + "domain-browser": "1.1.7", + "duplexer2": "0.1.4", + "events": "1.1.1", + "glob": "7.1.1", + "has": "1.0.1", + "htmlescape": "1.1.1", + "https-browserify": "1.0.0", + "inherits": "2.0.3", + "insert-module-globals": "7.0.1", + "labeled-stream-splicer": "2.0.0", + "module-deps": "4.1.1", + "os-browserify": "0.1.2", + "parents": "1.0.1", + "path-browserify": "0.0.0", + "process": "0.11.10", + "punycode": "1.4.1", + "querystring-es3": "0.2.1", + "read-only-stream": "2.0.0", + "readable-stream": "2.3.3", + "resolve": "1.4.0", + "shasum": "1.0.2", + "shell-quote": "1.6.1", + "stream-browserify": "2.0.1", + "stream-http": "2.7.2", + "string_decoder": "1.0.3", + "subarg": "1.0.0", + "syntax-error": "1.3.0", + "through2": "2.0.3", + "timers-browserify": "1.4.2", + "tty-browserify": "0.0.0", + "url": "0.11.0", + "util": "0.10.3", + "vm-browserify": "0.0.4", + "xtend": "4.0.1" + } + }, + "browserify-aes": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.8.tgz", + "integrity": "sha512-WYCMOT/PtGTlpOKFht0YJFYcPy6pLCR98CtWfzK13zoynLlBMvAdEMSRGmgnJCw2M2j/5qxBkinZQFobieM8dQ==", + "requires": { + "buffer-xor": "1.0.3", + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "browserify-cipher": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", + "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", + "requires": { + "browserify-aes": "1.0.8", + "browserify-des": "1.0.0", + "evp_bytestokey": "1.0.3" + } + }, + "browserify-des": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", + "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", + "requires": { + "cipher-base": "1.0.4", + "des.js": "1.0.0", + "inherits": "2.0.3" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "4.11.8", + "randombytes": "2.0.5" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "elliptic": "6.4.0", + "inherits": "2.0.3", + "parse-asn1": "5.1.0" + } + }, + "browserify-zlib": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", + "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", + "requires": { + "pako": "0.2.9" + } + }, + "buffer": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.0.7.tgz", + "integrity": "sha512-NeeHXWh5pCbPQCt2/6rLvXqapZfVsqw/YgRgaHpT3H9Uzgs+S0lSg5SQzouIuDvcmlQRqBe8hOO2scKCu3cxrg==", + "requires": { + "base64-js": "1.2.1", + "ieee754": "1.1.8" + } + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "cached-path-relative": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", + "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=" + }, + "catharsis": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", + "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", + "requires": { + "underscore-contrib": "0.3.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + }, + "combine-source-map": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", + "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=", + "requires": { + "convert-source-map": "1.1.3", + "inline-source-map": "0.6.2", + "lodash.memoize": "3.0.4", + "source-map": "0.5.7" + } + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz", + "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.0.6", + "typedarray": "0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "requires": { + "date-now": "0.1.4" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "convert-source-map": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", + "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "create-ecdh": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", + "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", + "requires": { + "bn.js": "4.11.8", + "elliptic": "6.4.0" + } + }, + "create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", + "requires": { + "cipher-base": "1.0.4", + "inherits": "2.0.3", + "ripemd160": "2.0.1", + "sha.js": "2.4.8" + } + }, + "create-hmac": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", + "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", + "requires": { + "cipher-base": "1.0.4", + "create-hash": "1.1.3", + "inherits": "2.0.3", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.8" + } + }, + "crypto-browserify": { + "version": "3.11.1", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.1.tgz", + "integrity": "sha512-Na7ZlwCOqoaW5RwUK1WpXws2kv8mNhWdTlzob0UXulk6G9BDbyiJaGTYBIX61Ozn9l1EPPJpICZb4DaOpT9NlQ==", + "requires": { + "browserify-cipher": "1.0.0", + "browserify-sign": "4.0.4", + "create-ecdh": "4.0.0", + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "diffie-hellman": "5.0.2", + "inherits": "2.0.3", + "pbkdf2": "3.0.14", + "public-encrypt": "4.0.0", + "randombytes": "2.0.5" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=" + }, + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "requires": { + "ms": "2.0.0" + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "deps-sort": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", + "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=", + "requires": { + "JSONStream": "1.3.1", + "shasum": "1.0.2", + "subarg": "1.0.0", + "through2": "2.0.3" + } + }, + "des.js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", + "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "detective": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz", + "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=", + "requires": { + "acorn": "4.0.13", + "defined": "1.0.0" + } + }, + "diff": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", + "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=" + }, + "diffie-hellman": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", + "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", + "requires": { + "bn.js": "4.11.8", + "miller-rabin": "4.0.0", + "randombytes": "2.0.5" + } + }, + "domain-browser": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", + "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "requires": { + "readable-stream": "2.3.3" + } + }, + "elliptic": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", + "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0", + "hash.js": "1.1.3", + "hmac-drbg": "1.0.1", + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "1.3.4", + "safe-buffer": "5.1.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "graceful-readlink": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", + "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" + }, + "growl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", + "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=" + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "requires": { + "function-bind": "1.1.1" + } + }, + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=" + }, + "hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", + "requires": { + "inherits": "2.0.3" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "2.0.3", + "minimalistic-assert": "1.0.0" + } + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "1.1.3", + "minimalistic-assert": "1.0.0", + "minimalistic-crypto-utils": "1.0.1" + } + }, + "htmlescape": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz", + "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=" + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "ieee754": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", + "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" + }, + "indexof": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", + "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "inline-source-map": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz", + "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=", + "requires": { + "source-map": "0.5.7" + } + }, + "insert-module-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", + "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", + "requires": { + "JSONStream": "1.3.1", + "combine-source-map": "0.7.2", + "concat-stream": "1.5.2", + "is-buffer": "1.1.5", + "lexical-scope": "1.2.0", + "process": "0.11.10", + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "is-buffer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", + "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "js2xmlparser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", + "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", + "requires": { + "xmlcreate": "1.0.2" + } + }, + "jsdoc": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", + "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", + "requires": { + "babylon": "7.0.0-beta.19", + "bluebird": "3.5.0", + "catharsis": "0.8.9", + "escape-string-regexp": "1.0.5", + "js2xmlparser": "3.0.0", + "klaw": "2.0.0", + "marked": "0.3.6", + "mkdirp": "0.5.1", + "requizzle": "0.2.1", + "strip-json-comments": "2.0.1", + "taffydb": "2.6.2", + "underscore": "1.8.3" + } + }, + "json-stable-stringify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz", + "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=", + "requires": { + "jsonify": "0.0.0" + } + }, + "json3": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", + "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=" + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "klaw": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", + "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", + "requires": { + "graceful-fs": "4.1.11" + } + }, + "labeled-stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz", + "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=", + "requires": { + "inherits": "2.0.3", + "isarray": "0.0.1", + "stream-splicer": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + } + } + }, + "lexical-scope": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", + "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=", + "requires": { + "astw": "2.2.0" + } + }, + "lodash._baseassign": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", + "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", + "requires": { + "lodash._basecopy": "3.0.1", + "lodash.keys": "3.1.2" + } + }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=" + }, + "lodash._basecreate": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", + "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=" + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" + }, + "lodash.create": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", + "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", + "requires": { + "lodash._baseassign": "3.2.0", + "lodash._basecreate": "3.0.3", + "lodash._isiterateecall": "3.0.9" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=" + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=" + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.memoize": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", + "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" + }, + "marked": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", + "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=" + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "requires": { + "hash-base": "3.0.4", + "inherits": "2.0.3" + }, + "dependencies": { + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "2.0.3", + "safe-buffer": "5.1.1" + } + } + } + }, + "miller-rabin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz", + "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=", + "requires": { + "bn.js": "4.11.8", + "brorand": "1.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", + "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", + "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", + "requires": { + "browser-stdout": "1.3.0", + "commander": "2.9.0", + "debug": "2.6.8", + "diff": "3.2.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.1", + "growl": "1.9.2", + "he": "1.1.1", + "json3": "3.3.2", + "lodash.create": "3.1.1", + "mkdirp": "0.5.1", + "supports-color": "3.1.2" + } + }, + "module-deps": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz", + "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", + "requires": { + "JSONStream": "1.3.1", + "browser-resolve": "1.11.2", + "cached-path-relative": "1.0.1", + "concat-stream": "1.5.2", + "defined": "1.0.0", + "detective": "4.5.0", + "duplexer2": "0.1.4", + "inherits": "2.0.3", + "parents": "1.0.1", + "readable-stream": "2.3.3", + "resolve": "1.4.0", + "stream-combiner2": "1.1.1", + "subarg": "1.0.0", + "through2": "2.0.3", + "xtend": "4.0.1" + } + }, + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "os-browserify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", + "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=" + }, + "pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=" + }, + "parents": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz", + "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=", + "requires": { + "path-platform": "0.11.15" + } + }, + "parse-asn1": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", + "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", + "requires": { + "asn1.js": "4.9.1", + "browserify-aes": "1.0.8", + "create-hash": "1.1.3", + "evp_bytestokey": "1.0.3", + "pbkdf2": "3.0.14" + } + }, + "path-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", + "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", + "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" + }, + "path-platform": { + "version": "0.11.15", + "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz", + "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=" + }, + "pbkdf2": { + "version": "3.0.14", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", + "integrity": "sha512-gjsZW9O34fm0R7PaLHRJmLLVfSoesxztjPjE9o6R+qtVJij90ltg1joIovN9GKrRW3t1PzhDDG3UMEMFfZ+1wA==", + "requires": { + "create-hash": "1.1.3", + "create-hmac": "1.1.6", + "ripemd160": "2.0.1", + "safe-buffer": "5.1.1", + "sha.js": "2.4.8" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "public-encrypt": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", + "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", + "requires": { + "bn.js": "4.11.8", + "browserify-rsa": "4.0.1", + "create-hash": "1.1.3", + "parse-asn1": "5.1.0", + "randombytes": "2.0.5" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", + "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "read-only-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz", + "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=", + "requires": { + "readable-stream": "2.3.3" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "requizzle": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", + "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "resolve": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", + "requires": { + "path-parse": "1.0.5" + } + }, + "ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", + "requires": { + "hash-base": "2.0.2", + "inherits": "2.0.3" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + }, + "sha.js": { + "version": "2.4.8", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", + "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=", + "requires": { + "inherits": "2.0.3" + } + }, + "shasum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz", + "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=", + "requires": { + "json-stable-stringify": "0.0.1", + "sha.js": "2.4.8" + } + }, + "shell-quote": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz", + "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=", + "requires": { + "array-filter": "0.0.1", + "array-map": "0.0.0", + "array-reduce": "0.0.0", + "jsonify": "0.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "stream-browserify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", + "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "stream-combiner2": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz", + "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=", + "requires": { + "duplexer2": "0.1.4", + "readable-stream": "2.3.3" + } + }, + "stream-http": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", + "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", + "requires": { + "builtin-status-codes": "3.0.0", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "to-arraybuffer": "1.0.1", + "xtend": "4.0.1" + } + }, + "stream-splicer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz", + "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=", + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "requires": { + "safe-buffer": "5.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "requires": { + "minimist": "1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "supports-color": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", + "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", + "requires": { + "has-flag": "1.0.0" + } + }, + "syntax-error": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz", + "integrity": "sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE=", + "requires": { + "acorn": "4.0.13" + } + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } + }, + "timers-browserify": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz", + "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=", + "requires": { + "process": "0.11.10" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "umd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz", + "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=" + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, + "underscore-contrib": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", + "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", + "requires": { + "underscore": "1.6.0" + }, + "dependencies": { + "underscore": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" + } + } + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "vm-browserify": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", + "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", + "requires": { + "indexof": "0.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xmlcreate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", + "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=" + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..36212fd --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "dependencies": [ + "moment" + ], + "devDependencies": [ + "mocha", + "browserify", + "jsdoc" + ], + "scripts": { + "test": "mocha" + } +} diff --git a/settings/en_US.html b/settings/en_US.html new file mode 100644 index 0000000..30a4532 --- /dev/null +++ b/settings/en_US.html @@ -0,0 +1,120 @@ + + + + + + + + +

Block Sets

+ +
    + +
+ + + + + + + diff --git a/settings/settings.css b/settings/settings.css new file mode 100644 index 0000000..22a7ea9 --- /dev/null +++ b/settings/settings.css @@ -0,0 +1,77 @@ +body { + max-width: 800px; + margin-left: auto; + margin-right: auto; +} + +#block-set-list { + list-style-type: none; + margin-top: 0; + padding: 0; + padding-left: 2em; + +} + +.block-set { + border-bottom-style: solid; + border-bottom-width: 1px; +} + +.block-set:last-child { + border-bottom-style: none; + border-bottom-width: 0; +} + +.form-group { + display: block; + margin-left: 0; + margin-right: 0; + margin-top: 1em; + margin-bottom: 1em; + border-width: 0; + padding: 0; +} + +.form-group ul { + display: inline-block; + list-style-type: none; + margin: 0; + padding: 0; +} +.form-group li { + margin-top: 0.5em; + margin-bottom: 0.5em; +} +.form-group li:first-child { + margin-top: 0; +} + +.form-group li:first-child { + margin-top:0; +} + +.form-group > label:first-child { + float: left; + width: 200px; +} +fieldset legend { + float:left; + width: 200px; + margin: 0; + padding: 0; +} + +[aria-invalid="true"] { + box-shadow: red 0px 0px 1.5px 1px +} + +[aria-invalid="false"] { + box-shadow: green 0px 0px 1.5px 1px +} + +input[type='number'] { + width: 100px; +} +input[type='time'] { + width: 100px; +} diff --git a/settings/settings.js b/settings/settings.js new file mode 100644 index 0000000..dc30053 --- /dev/null +++ b/settings/settings.js @@ -0,0 +1,296 @@ +'use strict' + +const { + getState, + setState, + formatISOTime, + parseISOTime, + exampleState +} = require('../lib/state.js') +const moment = require('moment') +const {matchPatternToRegExp} = require('../lib/match-pattern.js') + +function setIdPostfix(postfix, node) { + const ids = [...node.querySelectorAll("[id]")].map(n => n.getAttribute("id")) + + for (const id of ids) { + for (const attr of ["id", "for", "aria-describedby"]) { + for (const n of node.querySelectorAll("[" + attr + "=" + id + "]")) { + n.setAttribute(attr, id + "-" + postfix) + } + if (node.getAttribute(attr) === id) { + node.setAttribute(attr, id + "-" + postfix) + } + } + } +} + +function setParse(node, parse, parsedValue) { + node.parse = () => { + const parsedValue = parse(node.value) + if (parsedValue != undefined) { + node.removeAttribute('aria-invalid') + } else { + node.setAttribute('aria-invalid', true) + } + return parsedValue + } +} + +function validInput(input) { + return input.getAttribute('aria-invalid') !== true +} + +function expandTemplateChild(template) { + console.assert(template.content.children.length === 1) + const child = template.content.children[0].cloneNode(true) + if (template.parentNode != undefined) { + template.parentNode.insertBefore(child, template.nextSibling) + } + return child +} + +function fillMany(templateNode, fillElementFunc, elements) { + console.assert(templateNode.content.children.length === 1) + const childNode = templateNode.content.children[0] + + const newNodes = elements.map((el, i) => { + const childNode_ = childNode.cloneNode(true) + fillElementFunc(childNode_, el, i) + setIdPostfix(i, childNode_) + return childNode_ + }) + + const parentNode = templateNode.parentNode; + if (parentNode != undefined) { + let lastNode = templateNode + for (const newNode of newNodes) { + parentNode.insertBefore(newNode, lastNode.nextSibling) + lastNode = newNode + } + } + + return newNodes +} + +function parseTime(str) { + const formats = [ + "hh:mm:ss a", "HH:mm:ss", "hh:mm a", "HH:mm", "hh a", + "h:mm:ss a", "h:mm a", "h a" + ] + const m = moment(str, formats, true) + + if (m.isValid()) { + const time = { + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds() + } + if (Object.values(time).every(t => t === 0)) { + if (m > moment()) { + time.hours = 24 + } + } + return time + } else { + return undefined + } +} + +function parseMatchPattern0(str) { + const trimmed = str.trim() + if (trimmed === "" || matchPatternToRegExp(trimmed) != undefined) { + return trimmed + } else { + return undefined + } +} + +function fillBlockSet(blockSetNode, blockSetSettings) { + const node = blockSetNode + node.blockSetSettings = blockSetSettings + const settings = blockSetSettings + + const name = node.querySelector('#block-set-name') + name.value = settings.name + name.addEventListener('change', () => { + settings.name = name.value.trim() + }) + + const editBlockedPageTemplate = + node.querySelector('#edit-blocked-page-template') + function addBlockedUrlPattern(pattern) { + const container = expandTemplateChild(editBlockedPageTemplate) + const input = container.querySelector('input') + input.value = pattern; + setParse(input, parseMatchPattern0) + input.addEventListener("change", () => { + // index in editBlockedPageTemplate this element corresponds to + const i = [...container.parentNode.children].indexOf(container) - 2 + if (input.parse() != undefined) { + settings.blockedUrlPatterns[i] = input.parse() + } + if (input.parse() === "") { + settings.blockedUrlPatterns.splice(i, 1) + container.parentNode.removeChild(container) + } + }) + } + + const newBlockedPage = node.querySelector('#new-blocked-page') + newBlockedPage.value = "" + setParse(newBlockedPage, parseMatchPattern0) + newBlockedPage.addEventListener('change', () => { + if ( + newBlockedPage.parse() != undefined && + newBlockedPage.parse() !== "" + ) { + addBlockedUrlPattern(newBlockedPage.parse()) + settings.blockedUrlPatterns.unshift(newBlockedPage.parse()) + newBlockedPage.value = "" + } + }) + + for (const pattern of [...blockSetSettings.blockedUrlPatterns].reverse()) { + addBlockedUrlPattern(pattern) + } + + const startTime = node.querySelector('#start-time') + startTime.value = settings.startTime // TODO: localize + setParse(startTime, parseTime) + + const endTime = node.querySelector('#end-time') + endTime.value = settings.endTime // TODO: localize + setParse(endTime, parseTime) + + for (const time of [startTime, endTime]) { + time.addEventListener('change', () => { + if (startTime.parse() != undefined && endTime.parse() != undefined) { + if (moment(startTime.parse()) < moment(endTime.parse())) { + settings.startTime = formatISOTime(startTime.parse()) + settings.endTime = formatISOTime(endTime.parse()) + startTime.removeAttribute('aria-invalid') + endTime.removeAttribute('aria-invalid') + } else { + startTime.setAttribute('aria-invalid', true) + endTime.setAttribute('aria-invalid', true) + } + } else { + startTime.parse() + endTime.parse() + } + }) + } + + for (const i of [0, 1, 2, 3, 4, 5, 6]) { + const day = node.querySelector('#day' + i) + day.checked = settings.days[i] + day.addEventListener('change', () => { + settings.days[i] = day.checked + }) + } + + const quotaAllowedNumber = node.querySelector('#quota-allowed-number') + setParse(quotaAllowedNumber, s => { + const i = parseInt(s) + if (isNaN(i) || i < 0) { + return undefined + } else { + return i + } + }) + const quotaAllowedUnit = node.querySelector('#quota-allowed-unit') + const quotaAllowed = moment.duration(settings.quotaAllowed) + console.assert(moment.isDuration(quotaAllowed)) + quotaAllowedUnit.value = + ["hours", "minutes"]. + find(unit => Number.isInteger(quotaAllowed.as(unit))) + quotaAllowedNumber.value = quotaAllowed.as(quotaAllowedUnit.value) + for (const quotaAllowedNode of [quotaAllowedNumber, quotaAllowedUnit]) { + quotaAllowedNode.addEventListener('change', () => { + const number = quotaAllowedNumber.parse() + if (number != undefined) { + const unit = quotaAllowedUnit.value + settings.quotaAllowed = moment.duration(number, unit).toISOString() + } + }) + } + + const quotaIntervalNumber = node.querySelector('#quota-interval-number') + setParse(quotaIntervalNumber, s => { + const i = parseInt(s) + if (isNaN(i) || i <= 0) { + return undefined + } else { + return i + } + }) + const quotaIntervalUnit = node.querySelector('#quota-interval-unit') + const quotaInterval = moment.duration(settings.quotaInterval) + quotaIntervalUnit.value = + ["months", "weeks", "days", "hours", "minutes"]. + find(unit => Number.isInteger(quotaInterval.as(unit))) + quotaIntervalNumber.value = quotaInterval.as(quotaIntervalUnit.value) + for (const quotaIntervalNode of [quotaIntervalNumber, quotaIntervalUnit]) { + quotaIntervalNode.addEventListener('change', () => { + const number = quotaIntervalNumber.parse() + if (number != undefined) { + const unit = quotaIntervalUnit.value + settings.quotaInterval = moment.duration(number, unit).toISOString() + } + }) + } +} + +function fillBody(body, state) { + const blockSetTemplate = body.querySelector('#block-set-template') + for (const blockSetSettings of [...state.blockSetSettings].reverse()) { + const blockSetNode = expandTemplateChild(blockSetTemplate) + fillBlockSet(blockSetNode, blockSetSettings) + } +} + + +const settingsKeys = ["blockSetSettings"] +function getSettingsState() { + return getState().then(state => { + for (const key in state) { + if (settingsKeys.indexOf(key) === -1) { + delete state[key] + } + } + return state + }) +} + +if (typeof browser !== "undefined") { + getSettingsState().then(state => { + fillBody(document.body, state) + document.body.addEventListener('change', changes => { + setState(state) + }) + + + // reload the settings page if the settings state has changed on disk + browser.storage.onChanged.addListener(changes => { + if (settingsKeys.some(key => key in changes)) { + // so some key that affects settings has changed + getSettingsState().then(state_ => { + if (JSON.stringify(state_) !== JSON.stringify(state)) { + // the data is actually different, reload the settings page + location.reload() + } + }) + } + }) + }) +} else { + const state = { + blockSetSettings: exampleState.blockSetSettings + } + fillBody(document.body, state) + document.body.addEventListener('change', () => { + console.log(JSON.stringify(state)) + }) +} diff --git a/test/logic.js b/test/logic.js new file mode 100644 index 0000000..257f928 --- /dev/null +++ b/test/logic.js @@ -0,0 +1,237 @@ +const assert = require('assert') +const moment = require('moment') +const { + isInsideDailyInterval, + nextMomentInsideDailyInterval, + nextMomentOutsideDailyInterval, + nextIntervalBoundary, + previousIntervalBoundary +} = require('../lib/logic.js') + +moment.utc() + +describe('*dailyInterval', () => { + const friday = moment.utc("2017-09-08T12:34:56.789Z") // a Friday + + it('should work for a standard interval', () => { + const interval = { + startTime: { + hour: 4, + minute: 59, + second: 0, + millisecond: 0 + }, + endTime: { + hour: 6, + minute: 3, + second: 0, + millisecond: 0 + }, + days: [false, false, false, false, true, false, true] // Thursday and Saturday + } + let m; + + // Friday + m = friday.clone() + assert(!isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + "2017-09-09T04:59:00.000Z" + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + + // Saturday between 5 and 6 + m = friday.clone().day(6).hour(5) + assert(isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + "2017-09-09T06:03:00.000Z" + ) + + // Saturday exactly when the time interval ends + m = friday.clone().day(6).hour(6).minute(3).second(0).milliseconds(0) + assert(!isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + "2017-09-14T04:59:00.000Z" + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + + // Saturday exactly when the time interval begins + m = friday.clone().day(6).hour(4).minute(59).second(0).millisecond(0) + assert(isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + "2017-09-09T06:03:00.000Z" + ) + }) + + it('should work for an interval with no days at all', () => { + const interval = { + startTime: { + hour: 4, + minute: 59, + second: 0, + millisecond: 0 + }, + endTime: { + hour: 6, + minute: 3, + second: 0, + millisecond: 0 + }, + days: [false, false, false, false, false, false, false] + } + + assert(!isInsideDailyInterval(friday, interval)) + assert.equal( + nextMomentInsideDailyInterval(friday, interval), + undefined + ) + assert.equal( + nextMomentOutsideDailyInterval(friday, interval).toISOString(), + friday.toISOString() + ) + }) + + it('should work when time interval is empty', () => { + const interval = { + startTime: { + hour: 4, + minute: 59, + second: 0, + millisecond: 0 + }, + endTime: { + hour: 4, + minute: 59, + second: 0, + millisecond: 0 + }, + days: [0, 1, 2, 3, 4, 5, 6] + } + + assert(!isInsideDailyInterval(friday, interval)) + assert.equal( + nextMomentInsideDailyInterval(friday, interval), + undefined + ) + assert.equal( + nextMomentOutsideDailyInterval(friday, interval).toISOString(), + friday.toISOString() + ) + }) + + it('should work when time interval is 24h', () => { + const interval = { + startTime: { + hour: 0, + minute: 0, + second: 0, + millisecond: 0 + }, + endTime: { + hour: 24, + minute: 0, + second: 0, + millisecond: 0 + }, + //days: [0, 1, 3, 4, 6] + days: [true, true, false, true, true, false, true] + } + let m; + + m = friday.clone().day(2) + assert(!isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + m.clone().day(3).set(interval.startTime).toISOString() + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + + m = friday.clone().day(3) + assert(isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + m.clone().day(5).set(interval.startTime).toISOString() + ) + + m = friday.clone().day(6) + assert(isInsideDailyInterval(m, interval)) + assert.equal( + nextMomentInsideDailyInterval(m, interval).toISOString(), + m.toISOString() + ) + assert.equal( + nextMomentOutsideDailyInterval(m, interval).toISOString(), + m.clone().day(7 + 2).set(interval.startTime).toISOString() + ) + }) +}) + + +describe('[next|previous]Multiple', function() { + it("should work for start=now", () => { + const now = moment.utc("2017-09-08T12:34:56.789Z") + const start = now; + const duration = moment.duration(123); + + assert.equal( + nextIntervalBoundary(now, start, duration).toISOString(), + now.toISOString() + ) + assert.equal( + previousIntervalBoundary(now, start, duration).toISOString(), + now.toISOString() + ) + }) + it("should work for start < now", () => { + const now = moment.utc("2017-09-08T12:34:56.789Z") + const start = now.clone().subtract({days: 123, hours: 5}) + const interval = moment.duration(2, "hours") + + assert.equal( + nextIntervalBoundary(now, start, interval).toISOString(), + now.clone().add(1, "hours").toISOString() + ) + assert.equal( + previousIntervalBoundary(now, start, interval).toISOString(), + now.clone().subtract(1, "hours").toISOString() + ) + }) + it("should work for start > now", () => { + const now = moment.utc("2017-09-08T12:34:56.789Z") + const start = now.clone().add({days: 123, hours: 5}) + const interval = moment.duration(2, "hours") + + assert.equal( + nextIntervalBoundary(now, start, interval).toISOString(), + now.clone().add(1, "hours").toISOString() + ) + assert.equal( + previousIntervalBoundary(now, start, interval).toISOString(), + now.clone().subtract(1, "hours").toISOString() + ) + }) +}) From 38f20629938d60aceebbc940be5ed957cde008a8 Mon Sep 17 00:00:00 2001 From: Martin Bidlingmaier Date: Sat, 16 Sep 2017 14:09:13 +0200 Subject: [PATCH 2/4] Remove already bundled scripts from settings page --- settings/en_US.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/settings/en_US.html b/settings/en_US.html index 30a4532..6485660 100644 --- a/settings/en_US.html +++ b/settings/en_US.html @@ -112,9 +112,6 @@

When to Block

- - - From 523039089eedb76504682b40325482d203386334 Mon Sep 17 00:00:00 2001 From: Martin Bidlingmaier Date: Sat, 16 Sep 2017 14:12:48 +0200 Subject: [PATCH 3/4] Use standard js, postfix for block set settings --- background.js | 71 +-- block/block.js | 40 +- lib/logic.js | 68 +-- lib/match-pattern.js | 20 +- lib/state.js | 81 +-- package-lock.json | 1277 +++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- settings/settings.js | 115 ++-- test/logic.js | 56 +- 9 files changed, 1468 insertions(+), 263 deletions(-) diff --git a/background.js b/background.js index 2d9182d..d85ac12 100644 --- a/background.js +++ b/background.js @@ -1,4 +1,5 @@ 'use strict' +/* globals browser */ const { formatISODateTime, @@ -14,71 +15,49 @@ const { nextNonBlockingMoment } = require('./lib/logic') -function getActiveUrl() { +function getActiveUrl () { return browser.windows.getLastFocused({populate: true, windowTypes: ['normal']}).then(activeWindow => { if (activeWindow.focused === true) { - const activeTab = activeWindow.tabs.find(tab => tab.active); - if (activeTab != undefined) { - return activeTab.url; + const activeTab = activeWindow.tabs.find(tab => tab.active) + if (activeTab != null) { + return activeTab.url } } - return undefined; + return undefined }) } -function getAllTabs() { +function getAllTabs () { return browser.tabs.query({windowType: 'normal'}) } -function getAllUrls() { - return getAllTabs().then(tabs => tabs.map(tab => tab.url).filter(url => url != undefined)) +function getAllUrls () { + return getAllTabs().then(tabs => tabs.map(tab => tab.url).filter(url => url != null)) } -function encodeBlockInfo(now, blockSet, blockedUrl) { - let hash = "#"; - if (blockedUrl != undefined) { - hash += encodeURIComponent(blockedUrl) - } - - hash += "#"; - - const unblockingMoment = nextNonBlockingMoment(now, blockSet); - if (unblockingMoment != undefined) { - hash += encodeURIComponent(unblockingMoment.toISOString()); - } - - hash += "#" - - if (blockSet.name != undefined) { - hash += encodeURIComponent(blockSet.name) - } - - return hash; -} - -function blockTab(now, settings, data, tab) { - const blockPageUrl = browser.extension.getURL("block/en-US.html") - let hash = "#" - if (tab.url != undefined) { +function blockTab (now, settings, data, tab) { + const blockPageUrl = browser.extension.getURL('block/en-US.html') + let hash = '#' + if (tab.url != null) { hash += encodeURIComponent(tab.url) } - hash += "#" + hash += '#' const unblockingMoment = nextNonBlockingMoment(now, settings, data) - if (unblockingMoment != undefined) { + if (unblockingMoment != null) { hash += formatISODateTime(unblockingMoment) } - hash += "#" - if (settings.name.trim() !== "") { + hash += '#' + if (settings.name.trim() !== '') { hash += settings.name } browser.tabs.update(tab.id, {url: blockPageUrl + hash}) } -function main() { +function main () { getState().then(state => { syncState(state) @@ -97,10 +76,10 @@ function main() { const id = settings.id const data = state.blockSetData[id] const newStatus = currentStatus(now, settings, data) - if (newStatus === "blocking" && lastStatuses[id] !== "blocking") { + if (newStatus === 'blocking' && lastStatuses[id] !== 'blocking') { getAllTabs().then(tabs => { Promise.all(tabs.map(tab => { - if (tab.url != undefined && affectsUrl(tab.url, settings, data)) { + if (tab.url != null && affectsUrl(tab.url, settings, data)) { blockTab(now, settings, data, tab) } })) @@ -119,27 +98,27 @@ function main() { // block a tab if its url changed to something blocked browser.tabs.onUpdated.addListener((tabId, {url}, tab) => { - if (url == undefined) { + if (url == null) { return } - const now = moment(); + const now = moment() for (const settings of state.blockSetSettings) { const data = state.blockSetData[settings.id] if ( - currentStatus(now, settings, data) === "blocking" && + currentStatus(now, settings, data) === 'blocking' && affectsUrl(url, settings, data) ) { blockTab(now, settings, data, tab) } } - }); + }) }) } module.exports = {main} -if (typeof browser !== "undefined") { +if (typeof browser !== 'undefined') { // only executed in browser main() } diff --git a/block/block.js b/block/block.js index 503274a..c6000b1 100644 --- a/block/block.js +++ b/block/block.js @@ -1,21 +1,21 @@ 'use strict' -function decodeBlockInfo(hash) { - let [_, blockedUrl, unblockingDate, blockSetName] = hash.split("#"); +function decodeBlockInfo (hash) { + let [, blockedUrl, unblockingDate, blockSetName] = hash.split('#') - if (blockedUrl !== "") { + if (blockedUrl !== '') { blockedUrl = decodeURIComponent(blockedUrl) } else { blockedUrl = undefined } - if (unblockingDate !== "") { + if (unblockingDate !== '') { unblockingDate = new Date(decodeURIComponent(unblockingDate)) } else { unblockingDate = undefined } - if (blockSetName !== "") { + if (blockSetName !== '') { blockSetName = decodeURIComponent(blockSetName) } else { blockSetName = undefined @@ -28,41 +28,39 @@ function decodeBlockInfo(hash) { } } - const {blockedUrl, unblockingDate, blockSetName} = decodeBlockInfo(window.location.hash) const blockedUrlLink = document.getElementById('blocked-url-link') -if (blockedUrl != undefined) { +if (blockedUrl != null) { blockedUrlLink.appendChild(document.createTextNode(blockedUrl)) - blockedUrlLink.setAttribute("href", blockedUrl) + blockedUrlLink.setAttribute('href', blockedUrl) } else { - blockedUrlLink.style.display = 'none'; + blockedUrlLink.style.display = 'none' } -if (unblockingDate != undefined) { +if (unblockingDate != null) { const now = new Date() - if (unblockingDate < now && blockedUrl != undefined) { - window.location.href = blockedUrl; + if (unblockingDate < now && blockedUrl != null) { + window.location.href = blockedUrl } - let formatted; + let formatted if (unblockingDate.toDateString() === now.toDateString()) { - formatted = unblockingDate.toLocaleTimeString(); + formatted = unblockingDate.toLocaleTimeString() } else { - formatted = unblockingDate.toLocaleString(); + formatted = unblockingDate.toLocaleString() } document.getElementById('never-unblocked').style.display = 'none' - document.getElementById('unblock-time'). - appendChild(document.createTextNode(formatted)) + document.getElementById('unblock-time') + .appendChild(document.createTextNode(formatted)) } else { document.getElementById('sometime-unblocked').style.display = 'none' } -const blockSetInfo = document.getElementById('block-set-info') -if (blockSetName != undefined) { - document.getElementById('block-set-name'). - appendChild(document.createTextNode(blockSetName)) +if (blockSetName != null) { + document.getElementById('block-set-name') + .appendChild(document.createTextNode(blockSetName)) } else { document.getElementById('block-set-info').style.display = 'none' } diff --git a/lib/logic.js b/lib/logic.js index 11b50c4..0f4e58f 100644 --- a/lib/logic.js +++ b/lib/logic.js @@ -1,7 +1,7 @@ 'use strict' const {matchPatternToRegExp} = require('./match-pattern') -const moment = require('moment'); +const moment = require('moment') const {parseISOTime, parseISODateTime, formatISODateTime} = require('./state') /** @@ -18,15 +18,15 @@ const {parseISOTime, parseISODateTime, formatISODateTime} = require('./state') * @param {DailyInterval} * @return {boolean} */ -function isInsideDailyInterval(m, {startTime, endTime, days}) { +function isInsideDailyInterval (m, {startTime, endTime, days}) { if (!days[m.day()]) { return false } - const startm = m.clone().set(startTime); - const endm = m.clone().set(endTime); + const startm = m.clone().set(startTime) + const endm = m.clone().set(endTime) - return startm <= m && m < endm; + return startm <= m && m < endm } /** @@ -34,16 +34,16 @@ function isInsideDailyInterval(m, {startTime, endTime, days}) { * @param {DailyInterval} * @return {moment} */ -function nextMomentInsideDailyInterval(m, dailyInterval) { +function nextMomentInsideDailyInterval (m, dailyInterval) { if (isInsideDailyInterval(m, dailyInterval)) { return m.clone() } - const {startTime, endTime, days} = dailyInterval; + const {startTime, endTime, days} = dailyInterval if ( moment.duration(endTime).valueOf() === moment.duration(startTime).valueOf() ) { - return undefined; + return undefined } if (days[m.day()]) { @@ -68,11 +68,11 @@ function nextMomentInsideDailyInterval(m, dailyInterval) { * @param {DailyInterval} * @return {moment} */ -function nextMomentOutsideDailyInterval(m, dailyInterval) { +function nextMomentOutsideDailyInterval (m, dailyInterval) { if (!isInsideDailyInterval(m, dailyInterval)) { - return m.clone(); + return m.clone() } - const {startTime, endTime, days} = dailyInterval; + const {startTime, endTime, days} = dailyInterval if ( moment.duration(startTime).valueOf() === 0 && @@ -90,14 +90,14 @@ function nextMomentOutsideDailyInterval(m, dailyInterval) { return m.clone().set(endTime) } -function nextIntervalBoundary(now, start, duration) { +function nextIntervalBoundary (now, start, duration) { const result = start.clone() if (start <= now) { - while (result < now) { + while (result < now) { // eslint-disable-line no-unmodified-loop-condition result.add(duration) } } else { - while (result > now) { + while (result > now) { // eslint-disable-line no-unmodified-loop-condition result.subtract(duration) } result.add(duration) @@ -105,17 +105,17 @@ function nextIntervalBoundary(now, start, duration) { return result } -function previousIntervalBoundary(now, start, duration) { +function previousIntervalBoundary (now, start, duration) { return nextIntervalBoundary(now.clone().subtract(duration), start, duration) } -function affectsUrl(url, {blockedUrlPatterns}, {taintedUrls}) { +function affectsUrl (url, {blockedUrlPatterns}, {taintedUrls}) { const patterns = blockedUrlPatterns.concat(taintedUrls) return patterns.some(pattern => matchPatternToRegExp(pattern).test(url)) } -function currentStatus( +function currentStatus ( now, {startTime, endTime, days, quotaInterval, quotaAllowed}, {quotaUsed, lastQuotaReset} @@ -129,24 +129,24 @@ function currentStatus( lastQuotaReset = parseISODateTime(lastQuotaReset) if (!isInsideDailyInterval(now, {startTime, endTime, days})) { - return "inactive" + return 'inactive' } const nextQuotaReset = nextIntervalBoundary(now, lastQuotaReset, quotaInterval) if (nextQuotaReset <= now && quotaAllowed > moment.duration(0)) { - return "tracking" + return 'tracking' } if (nextQuotaReset > now && quotaAllowed > quotaUsed) { - return "tracking" + return 'tracking' } - return "blocking" + return 'blocking' } -function nextNonBlockingMoment(now, settings, data) { - if (currentStatus(now, settings, data) !== "blocking") { - return now; +function nextNonBlockingMoment (now, settings, data) { + if (currentStatus(now, settings, data) !== 'blocking') { + return now } const startTime = parseISOTime(settings.startTime) @@ -158,21 +158,21 @@ function nextNonBlockingMoment(now, settings, data) { const quotaReset = parseISODateTime(settings.quotaReset) const quotaInterval = moment.duration(settings.quotaInterval) - let nextReset = undefined + let nextReset if (quotaAllowed > moment.duration(0)) { nextReset = nextIntervalBoundary(now, quotaReset, quotaInterval) } - if (nextInactive == undefined) { - return nextReset; + if (nextInactive == null) { + return nextReset } - if (nextReset == undefined) { - return nextInactive; + if (nextReset == null) { + return nextInactive } - return moment.min([nextInactive, nextReset]); + return moment.min([nextInactive, nextReset]) } -function quotaTick(getActiveUrl, getAllUrls, now, settings, data) { +function quotaTick (getActiveUrl, getAllUrls, now, settings, data) { // reset the quota usage if next reset moment is not in the future anymore const quotaReset = parseISODateTime(settings.quotaReset) const lastQuotaReset = parseISODateTime(data.lastQuotaReset) @@ -186,16 +186,16 @@ function quotaTick(getActiveUrl, getAllUrls, now, settings, data) { } // increase quotaUsed if the blockset is currenlty in tracking status - if (currentStatus(now, settings, data) === "tracking") { + if (currentStatus(now, settings, data) === 'tracking') { return getActiveUrl().then(url => { - if (url != undefined && affectsUrl(url, settings, data)) { + if (url != null && affectsUrl(url, settings, data)) { data.quotaUsed = String(moment.duration(data.quotaUsed).add(1, 'seconds')) } return data }) } - return Promise.resolve(); + return Promise.resolve() } module.exports = { diff --git a/lib/match-pattern.js b/lib/match-pattern.js index d4b2d74..6f7200a 100644 --- a/lib/match-pattern.js +++ b/lib/match-pattern.js @@ -2,7 +2,7 @@ // matches all valid match patterns (except '') // and extracts [ , scheme, host, path, ] -const matchPattern = (/^(?:(\*|http|https|file|ftp|app):\/\/([^\/]+|)\/?(.*))$/i); +const matchPattern = (/^(?:(\*|http|https|file|ftp|app):\/\/([^/]+|)\/?(.*))$/i) /** * Transforms a valid match pattern into a regular expression @@ -12,20 +12,20 @@ const matchPattern = (/^(?:(\*|http|https|file|ftp|app):\/\/([^\/]+|)\/?(.*))$/i * @return {RegExp} The pattern's equivalent as a RegExp. * @throws {TypeError} If the pattern is not a valid MatchPattern */ -function matchPatternToRegExp(pattern) { +function matchPatternToRegExp (pattern) { if (pattern === '') { - return (/^(?:https?|file|ftp|app):\/\//); + return (/^(?:https?|file|ftp|app):\/\//) } - const match = matchPattern.exec(pattern); + const match = matchPattern.exec(pattern) if (!match) { return undefined } - const [ , scheme, host, path, ] = match; - return new RegExp('^(?:' - + (scheme === '*' ? 'https?' : escape(scheme)) + ':\\/\\/' - + (host === '*' ? "[^\\/]*" : escape(host).replace(/^\*\./g, '(?:[^\\/]+)?')) - + (path ? (path == '*' ? '(?:\\/.*)?' : ('\\/' + escape(path).replace(/\*/g, '.*'))) : '\\/?') - + ')$'); + const [ , scheme, host, path ] = match + return new RegExp('^(?:' + + (scheme === '*' ? 'https?' : escape(scheme)) + ':\\/\\/' + + (host === '*' ? '[^\\/]*' : escape(host).replace(/^\*\./g, '(?:[^\\/]+)?')) + + (path ? (path === '*' ? '(?:\\/.*)?' : ('\\/' + escape(path).replace(/\*/g, '.*'))) : '\\/?') + + ')$') } if (typeof module !== 'undefined') { diff --git a/lib/state.js b/lib/state.js index da40452..483514c 100644 --- a/lib/state.js +++ b/lib/state.js @@ -1,11 +1,12 @@ 'use strict' +/* globals browser */ // Utilities for handling persistent state const moment = require('moment') // parse and format day time in format HH:mm:ss -function parseISOTime(str) { - if (str === "24:00:00") { +function parseISOTime (str) { + if (str === '24:00:00') { // moment would say that this is 00:00:00 on the next day return { hours: 24, @@ -14,7 +15,7 @@ function parseISOTime(str) { milliseconds: 0 } } else { - const m = moment(str, "HH:mm:ss") + const m = moment(str, 'HH:mm:ss') return { hours: m.hours(), minutes: m.minutes(), @@ -23,18 +24,18 @@ function parseISOTime(str) { } } } -function formatISOTime(t) { +function formatISOTime (t) { if (t.hours === 24) { - return "24:00:00" + return '24:00:00' } else { - return moment(t).format("HH:mm:ss") + return moment(t).format('HH:mm:ss') } } // parse and format an ISO date and time without time zone into a moment (local time) -function parseISODateTime(str) { +function parseISODateTime (str) { return moment(str) } -function formatISODateTime(m) { +function formatISODateTime (m) { // TODO: make more robust return m.format().slice(0, -6) } @@ -57,41 +58,42 @@ function formatISODateTime(m) { */ const exampleBlockSetSettings = { - id: "cee7cf25-6513-4281-9e32-9ed464d96614", - name: "My block set", + id: 'cee7cf25-6513-4281-9e32-9ed464d96614', + name: 'My block set', - blockedUrlPatterns: ["*://does-not-exist.com/*", "*://www.heise.de/*"], + blockedUrlPatterns: ['*://does-not-exist.com/*', '*://www.heise.de/*'], - startTime: "00:00:00", - endTime: "24:00:00", + startTime: '00:00:00', + endTime: '24:00:00', days: [true, true, true, true, true, true, true], - quotaInterval: "P1Y", - quotaReset: "2017-09-14T10:27:22", - quotaAllowed: "PT1H30M" + quotaInterval: 'P1Y', + quotaReset: '2017-09-14T10:27:22', + quotaAllowed: 'PT1H30M' } // courtesy https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript -function uuidv4() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { - var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); - return v.toString(16); - }); +function uuidv4 () { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + const r = Math.random() * 16 | 0 + const v = c === 'x' ? r : (r & 0x3 | 0x8) + return v.toString(16) + }) } -function emptyBlockSet() { +function emptyBlockSet () { return { id: uuidv4(), - name: "", - + name: '', + blockedUrlPatterns: [], - startTime: "09:00:00", - endTime: "17:00:00", + startTime: '09:00:00', + endTime: '17:00:00', days: [false, true, true, true, true, true, false], - quotaInterval: "P1D", - quotaReset: "2017-09-14T00:00:00", - quotaAllowed: "PT10M" + quotaInterval: 'P1D', + quotaReset: '2017-09-14T00:00:00', + quotaAllowed: 'PT10M' } } @@ -103,9 +105,9 @@ function emptyBlockSet() { */ const exampleBlockSetData = { - quotaUsed: "PT1H29M58S", - lastQuotaReset: "2017-09-14T10:27:22", - taintedUrls: ["http://visited-from-does-not-exist.com"] + quotaUsed: 'PT1H29M58S', + lastQuotaReset: '2017-09-14T10:27:22', + taintedUrls: ['http://visited-from-does-not-exist.com'] } /** @@ -120,18 +122,18 @@ const exampleState = { } exampleState.blockSetData[exampleBlockSetSettings.id] = exampleBlockSetData -function defaultState() { +function defaultState () { return { blockSetSettings: [emptyBlockSet()], blockSetData: {} } } -function setState(state) { +function setState (state) { return browser.storage.local.set(state) } -function insertMissingBlockSetData(state) { +function insertMissingBlockSetData (state) { for (const settings of state.blockSetSettings) { if (!(settings.id in state.blockSetData)) { state.blockSetData[settings.id] = { @@ -143,8 +145,8 @@ function insertMissingBlockSetData(state) { } } -function getState() { - const keys = ["blockSetSettings", "blockSetData"] +function getState () { + const keys = ['blockSetSettings', 'blockSetData'] return browser.storage.local.get(keys).then( state => { if (keys.some(key => !(key in state))) { @@ -154,6 +156,7 @@ function getState() { } }, err => { + console.log(err) return defaultState() } ).then(state => { @@ -162,13 +165,13 @@ function getState() { }) } -function syncState(state) { +function syncState (state) { browser.storage.onChanged.addListener(changes => { for (const key in changes) { state[key] = changes[key].newValue } insertMissingBlockSetData(state) - }); + }) } module.exports = { diff --git a/package-lock.json b/package-lock.json index 69bcdc6..080c60e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,68 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=" }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "requires": { + "acorn": "3.3.0" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=" + } + } + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + }, + "dependencies": { + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + } + } + }, + "ajv-keywords": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", + "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=" + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "argparse": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", + "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", + "requires": { + "sprintf-js": "1.0.3" + } + }, "array-filter": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", @@ -31,6 +93,33 @@ "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz", "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=" }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array.prototype.find": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.0.4.tgz", + "integrity": "sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA=", + "requires": { + "define-properties": "1.1.2", + "es-abstract": "1.8.2" + } + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" + }, "asn1.js": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz", @@ -57,6 +146,16 @@ "acorn": "4.0.13" } }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "1.1.3", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + } + }, "babylon": { "version": "7.0.0-beta.19", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", @@ -149,7 +248,7 @@ "domain-browser": "1.1.7", "duplexer2": "0.1.4", "events": "1.1.1", - "glob": "7.1.1", + "glob": "7.1.2", "has": "1.0.1", "htmlescape": "1.1.1", "https-browserify": "1.0.0", @@ -260,6 +359,11 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=" + }, "builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -270,6 +374,19 @@ "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz", "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=" }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "requires": { + "callsites": "0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=" + }, "catharsis": { "version": "0.8.9", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", @@ -278,6 +395,25 @@ "underscore-contrib": "0.3.0" } }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -287,6 +423,34 @@ "safe-buffer": "5.1.1" } }, + "circular-json": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==" + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "requires": { + "restore-cursor": "1.0.1" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, "combine-source-map": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz", @@ -354,6 +518,11 @@ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=" + }, "convert-source-map": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz", @@ -414,6 +583,14 @@ "randombytes": "2.0.5" } }, + "d": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", + "requires": { + "es5-ext": "0.10.30" + } + }, "date-now": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", @@ -427,11 +604,57 @@ "ms": "2.0.0" } }, + "debug-log": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debug-log/-/debug-log-1.0.1.tgz", + "integrity": "sha1-IwdjLUwEOCuN+KMvcLiVBG1SdF8=" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "requires": { + "foreach": "2.0.5", + "object-keys": "1.0.11" + } + }, "defined": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" }, + "deglob": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/deglob/-/deglob-2.1.0.tgz", + "integrity": "sha1-TUSr4W7zLHebSXK9FBqAMlApoUo=", + "requires": { + "find-root": "1.1.0", + "glob": "7.1.2", + "ignore": "3.3.5", + "pkg-config": "1.1.1", + "run-parallel": "1.1.6", + "uniq": "1.0.1" + } + }, + "del": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", + "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", + "requires": { + "globby": "5.0.0", + "is-path-cwd": "1.0.0", + "is-path-in-cwd": "1.0.0", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "rimraf": "2.6.2" + } + }, "deps-sort": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz", @@ -476,6 +699,15 @@ "randombytes": "2.0.5" } }, + "doctrine": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", + "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + }, "domain-browser": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", @@ -503,11 +735,327 @@ "minimalistic-crypto-utils": "1.0.1" } }, + "error-ex": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", + "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", + "requires": { + "is-arrayish": "0.2.1" + } + }, + "es-abstract": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.8.2.tgz", + "integrity": "sha512-dvhwFL3yjQxNNsOWx6exMlaDrRHCRGMQlnx5lsXDCZ/J7G/frgIIl94zhZSp/galVAYp7VzPi1OrAHta89/yGQ==", + "requires": { + "es-to-primitive": "1.1.1", + "function-bind": "1.1.1", + "has": "1.0.1", + "is-callable": "1.1.3", + "is-regex": "1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "requires": { + "is-callable": "1.1.3", + "is-date-object": "1.0.1", + "is-symbol": "1.0.1" + } + }, + "es5-ext": { + "version": "0.10.30", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.30.tgz", + "integrity": "sha1-cUGhaDZpfbq/qq7uQUlc4p9SyTk=", + "requires": { + "es6-iterator": "2.0.1", + "es6-symbol": "3.1.1" + } + }, + "es6-iterator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", + "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.30", + "es6-symbol": "3.1.1" + } + }, + "es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.30", + "es6-iterator": "2.0.1", + "es6-set": "0.1.5", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-set": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", + "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.30", + "es6-iterator": "2.0.1", + "es6-symbol": "3.1.1", + "event-emitter": "0.3.5" + } + }, + "es6-symbol": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", + "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.30" + } + }, + "es6-weak-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", + "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.30", + "es6-iterator": "2.0.1", + "es6-symbol": "3.1.1" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, + "escope": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", + "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", + "requires": { + "es6-map": "0.1.5", + "es6-weak-map": "2.0.2", + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "eslint": { + "version": "3.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", + "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", + "requires": { + "babel-code-frame": "6.26.0", + "chalk": "1.1.3", + "concat-stream": "1.5.2", + "debug": "2.6.8", + "doctrine": "2.0.0", + "escope": "3.6.0", + "espree": "3.5.1", + "esquery": "1.0.0", + "estraverse": "4.2.0", + "esutils": "2.0.2", + "file-entry-cache": "2.0.0", + "glob": "7.1.2", + "globals": "9.18.0", + "ignore": "3.3.5", + "imurmurhash": "0.1.4", + "inquirer": "0.12.0", + "is-my-json-valid": "2.16.1", + "is-resolvable": "1.0.0", + "js-yaml": "3.10.0", + "json-stable-stringify": "1.0.1", + "levn": "0.3.0", + "lodash": "4.17.4", + "mkdirp": "0.5.1", + "natural-compare": "1.4.0", + "optionator": "0.8.2", + "path-is-inside": "1.0.2", + "pluralize": "1.2.1", + "progress": "1.1.8", + "require-uncached": "1.0.3", + "shelljs": "0.7.8", + "strip-bom": "3.0.0", + "strip-json-comments": "2.0.1", + "table": "3.8.3", + "text-table": "0.2.0", + "user-home": "2.0.0" + }, + "dependencies": { + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "0.0.0" + } + } + } + }, + "eslint-config-standard": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-10.2.1.tgz", + "integrity": "sha1-wGHk0GbzedwXzVYsZOgZtN1FRZE=" + }, + "eslint-config-standard-jsx": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-standard-jsx/-/eslint-config-standard-jsx-4.0.2.tgz", + "integrity": "sha512-F8fRh2WFnTek7dZH9ZaE0PCBwdVGkwVWZmizla/DDNOmg7Tx6B/IlK5+oYpiX29jpu73LszeJj5i1axEZv6VMw==" + }, + "eslint-import-resolver-node": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz", + "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=", + "requires": { + "debug": "2.6.8", + "object-assign": "4.1.1", + "resolve": "1.4.0" + } + }, + "eslint-module-utils": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz", + "integrity": "sha512-jDI/X5l/6D1rRD/3T43q8Qgbls2nq5km5KSqiwlyUbGo5+04fXhMKdCPhjwbqAa6HXWaMxj8Q4hQDIh7IadJQw==", + "requires": { + "debug": "2.6.8", + "pkg-dir": "1.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz", + "integrity": "sha1-crowb60wXWfEgWNIpGmaQimsi04=", + "requires": { + "builtin-modules": "1.1.1", + "contains-path": "0.1.0", + "debug": "2.6.8", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "0.2.3", + "eslint-module-utils": "2.1.1", + "has": "1.0.1", + "lodash.cond": "4.5.2", + "minimatch": "3.0.4", + "pkg-up": "1.0.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-4.2.3.tgz", + "integrity": "sha512-vIUQPuwbVYdz/CYnlTLsJrRy7iXHQjdEe5wz0XhhdTym3IInM/zZLlPf9nZ2mThsH0QcsieCOWs2vOeCy/22LQ==", + "requires": { + "ignore": "3.3.5", + "minimatch": "3.0.4", + "object-assign": "4.1.1", + "resolve": "1.4.0", + "semver": "5.3.0" + } + }, + "eslint-plugin-promise": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-3.5.0.tgz", + "integrity": "sha1-ePu2/+BHIBYnVp6FpsU3OvKmj8o=" + }, + "eslint-plugin-react": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-6.10.3.tgz", + "integrity": "sha1-xUNb6wZ3ThLH2y9qut3L+QDNP3g=", + "requires": { + "array.prototype.find": "2.0.4", + "doctrine": "1.5.0", + "has": "1.0.1", + "jsx-ast-utils": "1.4.1", + "object.assign": "4.0.4" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "requires": { + "esutils": "2.0.2", + "isarray": "1.0.0" + } + } + } + }, + "eslint-plugin-standard": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-standard/-/eslint-plugin-standard-3.0.1.tgz", + "integrity": "sha1-NNDJFbRe3G8BA5PH7vOCOwhWXPI=" + }, + "espree": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz", + "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=", + "requires": { + "acorn": "5.1.2", + "acorn-jsx": "3.0.1" + }, + "dependencies": { + "acorn": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz", + "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==" + } + } + }, + "esprima": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", + "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==" + }, + "esquery": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", + "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", + "requires": { + "estraverse": "4.2.0" + } + }, + "esrecurse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", + "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", + "requires": { + "estraverse": "4.2.0", + "object-assign": "4.1.1" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "requires": { + "d": "1.0.0", + "es5-ext": "0.10.30" + } + }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -522,6 +1070,64 @@ "safe-buffer": "5.1.1" } }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "requires": { + "escape-string-regexp": "1.0.5", + "object-assign": "4.1.1" + } + }, + "file-entry-cache": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", + "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", + "requires": { + "flat-cache": "1.2.2", + "object-assign": "4.1.1" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "flat-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", + "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", + "requires": { + "circular-json": "0.3.3", + "del": "2.2.2", + "graceful-fs": "4.1.11", + "write": "0.2.1" + } + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -532,10 +1138,28 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=" + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "requires": { + "is-property": "1.0.2" + } + }, + "get-stdin": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", + "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=" + }, "glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", - "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "requires": { "fs.realpath": "1.0.0", "inflight": "1.0.6", @@ -545,6 +1169,24 @@ "path-is-absolute": "1.0.1" } }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "requires": { + "array-union": "1.0.2", + "arrify": "1.0.1", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, "graceful-fs": { "version": "4.1.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", @@ -568,6 +1210,14 @@ "function-bind": "1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "2.1.1" + } + }, "has-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", @@ -620,6 +1270,16 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=" }, + "ignore": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz", + "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, "indexof": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", @@ -647,6 +1307,26 @@ "source-map": "0.5.7" } }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "requires": { + "ansi-escapes": "1.4.0", + "ansi-regex": "2.1.1", + "chalk": "1.1.3", + "cli-cursor": "1.0.2", + "cli-width": "2.2.0", + "figures": "1.7.0", + "lodash": "4.17.4", + "readline2": "1.0.1", + "run-async": "0.1.0", + "rx-lite": "3.1.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "through": "2.3.8" + } + }, "insert-module-globals": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz", @@ -662,16 +1342,116 @@ "xtend": "4.0.1" } }, + "interpret": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", + "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, "is-buffer": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=" }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=" + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-my-json-valid": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", + "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, + "is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + }, + "is-path-in-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", + "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", + "requires": { + "is-path-inside": "1.0.0" + } + }, + "is-path-inside": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", + "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", + "requires": { + "path-is-inside": "1.0.2" + } + }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=" + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "1.0.1" + } + }, + "is-resolvable": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", + "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", + "requires": { + "tryit": "1.0.3" + } + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "js-yaml": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", + "requires": { + "argparse": "1.0.9", + "esprima": "4.0.0" + } + }, "js2xmlparser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", @@ -722,6 +1502,16 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=" + }, + "jsx-ast-utils": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-1.4.1.tgz", + "integrity": "sha1-OGchPo3Xm/Ho8jAMDPwe+xgsDfE=" + }, "klaw": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", @@ -747,6 +1537,15 @@ } } }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "requires": { + "prelude-ls": "1.1.2", + "type-check": "0.3.2" + } + }, "lexical-scope": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz", @@ -755,6 +1554,38 @@ "astw": "2.2.0" } }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "strip-bom": "3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "2.0.0", + "path-exists": "3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=" + }, "lodash._baseassign": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", @@ -784,6 +1615,11 @@ "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=" }, + "lodash.cond": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz", + "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=" + }, "lodash.create": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", @@ -872,9 +1708,9 @@ } }, "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, "mkdirp": { "version": "0.5.1", @@ -882,6 +1718,13 @@ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } } }, "mocha": { @@ -901,6 +1744,21 @@ "lodash.create": "3.1.1", "mkdirp": "0.5.1", "supports-color": "3.1.2" + }, + "dependencies": { + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + } } }, "module-deps": { @@ -935,6 +1793,41 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=" + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=" + }, + "object.assign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", + "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", + "requires": { + "define-properties": "1.1.2", + "function-bind": "1.1.1", + "object-keys": "1.0.11" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -943,11 +1836,47 @@ "wrappy": "1.0.2" } }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "requires": { + "deep-is": "0.1.3", + "fast-levenshtein": "2.0.6", + "levn": "0.3.0", + "prelude-ls": "1.1.2", + "type-check": "0.3.2", + "wordwrap": "1.0.0" + } + }, "os-browserify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz", "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=" }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "p-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", + "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=" + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "1.1.0" + } + }, "pako": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", @@ -973,16 +1902,37 @@ "pbkdf2": "3.0.14" } }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "1.3.1" + } + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=" }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "requires": { + "pinkie-promise": "2.0.1" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, "path-parse": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", @@ -1005,6 +1955,79 @@ "sha.js": "2.4.8" } }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "2.0.4" + } + }, + "pkg-conf": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.0.0.tgz", + "integrity": "sha1-BxyHZQQDvM+5xif1h1G/5HwGcnk=", + "requires": { + "find-up": "2.1.0", + "load-json-file": "2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "2.0.0" + } + } + } + }, + "pkg-config": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pkg-config/-/pkg-config-1.1.1.tgz", + "integrity": "sha1-VX7yLXPaPIg3EHdmxS6tq94pj+Q=", + "requires": { + "debug-log": "1.0.1", + "find-root": "1.1.0", + "xtend": "4.0.1" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "requires": { + "find-up": "1.1.2" + } + }, + "pkg-up": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-1.0.0.tgz", + "integrity": "sha1-Pgj7RhUlxEIWJKM7n35tCvWwWiY=", + "requires": { + "find-up": "1.1.2" + } + }, + "pluralize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", + "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=" + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + }, "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -1015,6 +2038,11 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" }, + "progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" + }, "public-encrypt": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", @@ -1072,6 +2100,33 @@ "util-deprecate": "1.0.2" } }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "mute-stream": "0.0.5" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "requires": { + "resolve": "1.4.0" + } + }, + "require-uncached": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", + "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", + "requires": { + "caller-path": "0.1.0", + "resolve-from": "1.0.1" + } + }, "requizzle": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", @@ -1095,6 +2150,28 @@ "path-parse": "1.0.5" } }, + "resolve-from": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", + "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=" + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "requires": { + "exit-hook": "1.1.1", + "onetime": "1.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "requires": { + "glob": "7.1.2" + } + }, "ripemd160": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", @@ -1104,11 +2181,34 @@ "inherits": "2.0.3" } }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "requires": { + "once": "1.4.0" + } + }, + "run-parallel": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.6.tgz", + "integrity": "sha1-KQA8miFj4B4tLfyQV18sbB1hoDk=" + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=" + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + }, "sha.js": { "version": "2.4.8", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz", @@ -1137,11 +2237,58 @@ "jsonify": "0.0.0" } }, + "shelljs": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", + "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", + "requires": { + "glob": "7.1.2", + "interpret": "1.0.4", + "rechoir": "0.6.2" + } + }, + "slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=" + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "standard": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/standard/-/standard-10.0.3.tgz", + "integrity": "sha512-JURZ+85ExKLQULckDFijdX5WHzN6RC7fgiZNSV4jFQVo+3tPoQGHyBrGekye/yf0aOfb4210EM5qPNlc2cRh4w==", + "requires": { + "eslint": "3.19.0", + "eslint-config-standard": "10.2.1", + "eslint-config-standard-jsx": "4.0.2", + "eslint-plugin-import": "2.2.0", + "eslint-plugin-node": "4.2.3", + "eslint-plugin-promise": "3.5.0", + "eslint-plugin-react": "6.10.3", + "eslint-plugin-standard": "3.0.1", + "standard-engine": "7.0.0" + } + }, + "standard-engine": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/standard-engine/-/standard-engine-7.0.0.tgz", + "integrity": "sha1-67d7nI/CyBZf+jU72Rug3/Qa9pA=", + "requires": { + "deglob": "2.1.0", + "get-stdin": "5.0.1", + "minimist": "1.2.0", + "pkg-conf": "2.0.0" + } + }, "stream-browserify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", @@ -1181,6 +2328,16 @@ "readable-stream": "2.3.3" } }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, "string_decoder": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", @@ -1189,6 +2346,19 @@ "safe-buffer": "5.1.1" } }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -1200,13 +2370,6 @@ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", "requires": { "minimist": "1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - } } }, "supports-color": { @@ -1225,11 +2388,58 @@ "acorn": "4.0.13" } }, + "table": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", + "requires": { + "ajv": "4.11.8", + "ajv-keywords": "1.5.1", + "chalk": "1.1.3", + "lodash": "4.17.4", + "slice-ansi": "0.0.4", + "string-width": "2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "3.0.0" + } + } + } + }, "taffydb": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -1257,11 +2467,24 @@ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" }, + "tryit": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", + "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=" + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "requires": { + "prelude-ls": "1.1.2" + } + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -1292,6 +2515,11 @@ } } }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=" + }, "url": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", @@ -1308,6 +2536,14 @@ } } }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "requires": { + "os-homedir": "1.0.2" + } + }, "util": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", @@ -1336,11 +2572,24 @@ "indexof": "0.0.1" } }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, + "write": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", + "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", + "requires": { + "mkdirp": "0.5.1" + } + }, "xmlcreate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", diff --git a/package.json b/package.json index 36212fd..2e2dd41 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "devDependencies": [ "mocha", "browserify", - "jsdoc" + "jsdoc", + "standard" ], "scripts": { "test": "mocha" diff --git a/settings/settings.js b/settings/settings.js index dc30053..da547fe 100644 --- a/settings/settings.js +++ b/settings/settings.js @@ -1,34 +1,35 @@ 'use strict' +/* globals browser, location */ + const { getState, setState, formatISOTime, - parseISOTime, exampleState } = require('../lib/state.js') const moment = require('moment') const {matchPatternToRegExp} = require('../lib/match-pattern.js') -function setIdPostfix(postfix, node) { - const ids = [...node.querySelectorAll("[id]")].map(n => n.getAttribute("id")) +function setIdPostfix (postfix, node) { + const ids = [...node.querySelectorAll('[id]')].map(n => n.getAttribute('id')) for (const id of ids) { - for (const attr of ["id", "for", "aria-describedby"]) { - for (const n of node.querySelectorAll("[" + attr + "=" + id + "]")) { - n.setAttribute(attr, id + "-" + postfix) + for (const attr of ['id', 'for', 'aria-describedby']) { + for (const n of node.querySelectorAll('[' + attr + '=' + id + ']')) { + n.setAttribute(attr, id + '-' + postfix) } if (node.getAttribute(attr) === id) { - node.setAttribute(attr, id + "-" + postfix) + node.setAttribute(attr, id + '-' + postfix) } } } } -function setParse(node, parse, parsedValue) { +function setParse (node, parse, parsedValue) { node.parse = () => { const parsedValue = parse(node.value) - if (parsedValue != undefined) { + if (parsedValue != null) { node.removeAttribute('aria-invalid') } else { node.setAttribute('aria-invalid', true) @@ -37,49 +38,22 @@ function setParse(node, parse, parsedValue) { } } -function validInput(input) { - return input.getAttribute('aria-invalid') !== true -} - -function expandTemplateChild(template) { +function expandTemplateChild (template) { console.assert(template.content.children.length === 1) const child = template.content.children[0].cloneNode(true) - if (template.parentNode != undefined) { + if (template.parentNode != null) { template.parentNode.insertBefore(child, template.nextSibling) } return child } -function fillMany(templateNode, fillElementFunc, elements) { - console.assert(templateNode.content.children.length === 1) - const childNode = templateNode.content.children[0] - - const newNodes = elements.map((el, i) => { - const childNode_ = childNode.cloneNode(true) - fillElementFunc(childNode_, el, i) - setIdPostfix(i, childNode_) - return childNode_ - }) - - const parentNode = templateNode.parentNode; - if (parentNode != undefined) { - let lastNode = templateNode - for (const newNode of newNodes) { - parentNode.insertBefore(newNode, lastNode.nextSibling) - lastNode = newNode - } - } - - return newNodes -} - -function parseTime(str) { +function parseTime (str) { const formats = [ - "hh:mm:ss a", "HH:mm:ss", "hh:mm a", "HH:mm", "hh a", - "h:mm:ss a", "h:mm a", "h a" + 'hh:mm:ss a', 'HH:mm:ss', 'hh:mm a', 'HH:mm', 'hh a', + 'h:mm:ss a', 'h:mm a', 'h a' ] const m = moment(str, formats, true) - + if (m.isValid()) { const time = { hours: m.hours(), @@ -98,16 +72,16 @@ function parseTime(str) { } } -function parseMatchPattern0(str) { +function parseMatchPattern0 (str) { const trimmed = str.trim() - if (trimmed === "" || matchPatternToRegExp(trimmed) != undefined) { + if (trimmed === '' || matchPatternToRegExp(trimmed) != null) { return trimmed } else { return undefined } } -function fillBlockSet(blockSetNode, blockSetSettings) { +function fillBlockSet (blockSetNode, blockSetSettings) { const node = blockSetNode node.blockSetSettings = blockSetSettings const settings = blockSetSettings @@ -120,35 +94,35 @@ function fillBlockSet(blockSetNode, blockSetSettings) { const editBlockedPageTemplate = node.querySelector('#edit-blocked-page-template') - function addBlockedUrlPattern(pattern) { + function addBlockedUrlPattern (pattern) { const container = expandTemplateChild(editBlockedPageTemplate) const input = container.querySelector('input') - input.value = pattern; + input.value = pattern setParse(input, parseMatchPattern0) - input.addEventListener("change", () => { + input.addEventListener('change', () => { // index in editBlockedPageTemplate this element corresponds to const i = [...container.parentNode.children].indexOf(container) - 2 - if (input.parse() != undefined) { + if (input.parse() != null) { settings.blockedUrlPatterns[i] = input.parse() } - if (input.parse() === "") { + if (input.parse() === '') { settings.blockedUrlPatterns.splice(i, 1) container.parentNode.removeChild(container) } }) } - + const newBlockedPage = node.querySelector('#new-blocked-page') - newBlockedPage.value = "" + newBlockedPage.value = '' setParse(newBlockedPage, parseMatchPattern0) newBlockedPage.addEventListener('change', () => { if ( - newBlockedPage.parse() != undefined && - newBlockedPage.parse() !== "" + newBlockedPage.parse() != null && + newBlockedPage.parse() !== '' ) { addBlockedUrlPattern(newBlockedPage.parse()) settings.blockedUrlPatterns.unshift(newBlockedPage.parse()) - newBlockedPage.value = "" + newBlockedPage.value = '' } }) @@ -166,7 +140,7 @@ function fillBlockSet(blockSetNode, blockSetSettings) { for (const time of [startTime, endTime]) { time.addEventListener('change', () => { - if (startTime.parse() != undefined && endTime.parse() != undefined) { + if (startTime.parse() != null && endTime.parse() != null) { if (moment(startTime.parse()) < moment(endTime.parse())) { settings.startTime = formatISOTime(startTime.parse()) settings.endTime = formatISOTime(endTime.parse()) @@ -203,14 +177,14 @@ function fillBlockSet(blockSetNode, blockSetSettings) { const quotaAllowedUnit = node.querySelector('#quota-allowed-unit') const quotaAllowed = moment.duration(settings.quotaAllowed) console.assert(moment.isDuration(quotaAllowed)) - quotaAllowedUnit.value = - ["hours", "minutes"]. - find(unit => Number.isInteger(quotaAllowed.as(unit))) + quotaAllowedUnit.value = + ['hours', 'minutes'] + .find(unit => Number.isInteger(quotaAllowed.as(unit))) quotaAllowedNumber.value = quotaAllowed.as(quotaAllowedUnit.value) for (const quotaAllowedNode of [quotaAllowedNumber, quotaAllowedUnit]) { quotaAllowedNode.addEventListener('change', () => { const number = quotaAllowedNumber.parse() - if (number != undefined) { + if (number != null) { const unit = quotaAllowedUnit.value settings.quotaAllowed = moment.duration(number, unit).toISOString() } @@ -228,14 +202,14 @@ function fillBlockSet(blockSetNode, blockSetSettings) { }) const quotaIntervalUnit = node.querySelector('#quota-interval-unit') const quotaInterval = moment.duration(settings.quotaInterval) - quotaIntervalUnit.value = - ["months", "weeks", "days", "hours", "minutes"]. - find(unit => Number.isInteger(quotaInterval.as(unit))) + quotaIntervalUnit.value = + ['months', 'weeks', 'days', 'hours', 'minutes'] + .find(unit => Number.isInteger(quotaInterval.as(unit))) quotaIntervalNumber.value = quotaInterval.as(quotaIntervalUnit.value) for (const quotaIntervalNode of [quotaIntervalNumber, quotaIntervalUnit]) { quotaIntervalNode.addEventListener('change', () => { const number = quotaIntervalNumber.parse() - if (number != undefined) { + if (number != null) { const unit = quotaIntervalUnit.value settings.quotaInterval = moment.duration(number, unit).toISOString() } @@ -243,17 +217,17 @@ function fillBlockSet(blockSetNode, blockSetSettings) { } } -function fillBody(body, state) { +function fillBody (body, state) { const blockSetTemplate = body.querySelector('#block-set-template') for (const blockSetSettings of [...state.blockSetSettings].reverse()) { const blockSetNode = expandTemplateChild(blockSetTemplate) fillBlockSet(blockSetNode, blockSetSettings) + setIdPostfix(blockSetSettings.id, blockSetNode) } } - -const settingsKeys = ["blockSetSettings"] -function getSettingsState() { +const settingsKeys = ['blockSetSettings'] +function getSettingsState () { return getState().then(state => { for (const key in state) { if (settingsKeys.indexOf(key) === -1) { @@ -264,13 +238,12 @@ function getSettingsState() { }) } -if (typeof browser !== "undefined") { +if (typeof browser !== 'undefined') { getSettingsState().then(state => { fillBody(document.body, state) document.body.addEventListener('change', changes => { setState(state) }) - // reload the settings page if the settings state has changed on disk browser.storage.onChanged.addListener(changes => { @@ -293,4 +266,4 @@ if (typeof browser !== "undefined") { document.body.addEventListener('change', () => { console.log(JSON.stringify(state)) }) -} +} diff --git a/test/logic.js b/test/logic.js index 257f928..a1a1e7a 100644 --- a/test/logic.js +++ b/test/logic.js @@ -1,3 +1,6 @@ +'use strict' +/* globals describe, it */ + const assert = require('assert') const moment = require('moment') const { @@ -11,7 +14,7 @@ const { moment.utc() describe('*dailyInterval', () => { - const friday = moment.utc("2017-09-08T12:34:56.789Z") // a Friday + const friday = moment.utc('2017-09-08T12:34:56.789Z') // a Friday it('should work for a standard interval', () => { const interval = { @@ -29,14 +32,14 @@ describe('*dailyInterval', () => { }, days: [false, false, false, false, true, false, true] // Thursday and Saturday } - let m; + let m // Friday m = friday.clone() assert(!isInsideDailyInterval(m, interval)) assert.equal( nextMomentInsideDailyInterval(m, interval).toISOString(), - "2017-09-09T04:59:00.000Z" + '2017-09-09T04:59:00.000Z' ) assert.equal( nextMomentOutsideDailyInterval(m, interval).toISOString(), @@ -52,7 +55,7 @@ describe('*dailyInterval', () => { ) assert.equal( nextMomentOutsideDailyInterval(m, interval).toISOString(), - "2017-09-09T06:03:00.000Z" + '2017-09-09T06:03:00.000Z' ) // Saturday exactly when the time interval ends @@ -60,7 +63,7 @@ describe('*dailyInterval', () => { assert(!isInsideDailyInterval(m, interval)) assert.equal( nextMomentInsideDailyInterval(m, interval).toISOString(), - "2017-09-14T04:59:00.000Z" + '2017-09-14T04:59:00.000Z' ) assert.equal( nextMomentOutsideDailyInterval(m, interval).toISOString(), @@ -76,7 +79,7 @@ describe('*dailyInterval', () => { ) assert.equal( nextMomentOutsideDailyInterval(m, interval).toISOString(), - "2017-09-09T06:03:00.000Z" + '2017-09-09T06:03:00.000Z' ) }) @@ -96,7 +99,7 @@ describe('*dailyInterval', () => { }, days: [false, false, false, false, false, false, false] } - + assert(!isInsideDailyInterval(friday, interval)) assert.equal( nextMomentInsideDailyInterval(friday, interval), @@ -124,7 +127,7 @@ describe('*dailyInterval', () => { }, days: [0, 1, 2, 3, 4, 5, 6] } - + assert(!isInsideDailyInterval(friday, interval)) assert.equal( nextMomentInsideDailyInterval(friday, interval), @@ -150,11 +153,11 @@ describe('*dailyInterval', () => { second: 0, millisecond: 0 }, - //days: [0, 1, 3, 4, 6] + // days: [0, 1, 3, 4, 6] days: [true, true, false, true, true, false, true] } - let m; - + let m + m = friday.clone().day(2) assert(!isInsideDailyInterval(m, interval)) assert.equal( @@ -190,12 +193,11 @@ describe('*dailyInterval', () => { }) }) - -describe('[next|previous]Multiple', function() { - it("should work for start=now", () => { - const now = moment.utc("2017-09-08T12:34:56.789Z") - const start = now; - const duration = moment.duration(123); +describe('[next|previous]Multiple', function () { + it('should work for start=now', () => { + const now = moment.utc('2017-09-08T12:34:56.789Z') + const start = now + const duration = moment.duration(123) assert.equal( nextIntervalBoundary(now, start, duration).toISOString(), @@ -206,32 +208,32 @@ describe('[next|previous]Multiple', function() { now.toISOString() ) }) - it("should work for start < now", () => { - const now = moment.utc("2017-09-08T12:34:56.789Z") + it('should work for start < now', () => { + const now = moment.utc('2017-09-08T12:34:56.789Z') const start = now.clone().subtract({days: 123, hours: 5}) - const interval = moment.duration(2, "hours") + const interval = moment.duration(2, 'hours') assert.equal( nextIntervalBoundary(now, start, interval).toISOString(), - now.clone().add(1, "hours").toISOString() + now.clone().add(1, 'hours').toISOString() ) assert.equal( previousIntervalBoundary(now, start, interval).toISOString(), - now.clone().subtract(1, "hours").toISOString() + now.clone().subtract(1, 'hours').toISOString() ) }) - it("should work for start > now", () => { - const now = moment.utc("2017-09-08T12:34:56.789Z") + it('should work for start > now', () => { + const now = moment.utc('2017-09-08T12:34:56.789Z') const start = now.clone().add({days: 123, hours: 5}) - const interval = moment.duration(2, "hours") + const interval = moment.duration(2, 'hours') assert.equal( nextIntervalBoundary(now, start, interval).toISOString(), - now.clone().add(1, "hours").toISOString() + now.clone().add(1, 'hours').toISOString() ) assert.equal( previousIntervalBoundary(now, start, interval).toISOString(), - now.clone().subtract(1, "hours").toISOString() + now.clone().subtract(1, 'hours').toISOString() ) }) }) From 58948fd25e63fd20ca4e1811316bc8b6424e7365 Mon Sep 17 00:00:00 2001 From: Martin Bidlingmaier Date: Sat, 16 Sep 2017 16:51:48 +0200 Subject: [PATCH 4/4] Support multiple block sets, minor style tweaks --- lib/state.js | 5 +++-- settings/en_US.html | 7 ++++++- settings/settings.css | 23 ++++++++++++++++------- settings/settings.js | 37 ++++++++++++++++++++++++++++--------- 4 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/state.js b/lib/state.js index 483514c..c6f1dcb 100644 --- a/lib/state.js +++ b/lib/state.js @@ -81,7 +81,7 @@ function uuidv4 () { }) } -function emptyBlockSet () { +function emptyBlockSetSettings () { return { id: uuidv4(), name: '', @@ -124,7 +124,7 @@ exampleState.blockSetData[exampleBlockSetSettings.id] = exampleBlockSetData function defaultState () { return { - blockSetSettings: [emptyBlockSet()], + blockSetSettings: [emptyBlockSetSettings()], blockSetData: {} } } @@ -182,6 +182,7 @@ module.exports = { getState, setState, exampleState, + emptyBlockSetSettings, defaultState, syncState } diff --git a/settings/en_US.html b/settings/en_US.html index 6485660..751db26 100644 --- a/settings/en_US.html +++ b/settings/en_US.html @@ -10,7 +10,7 @@

Block Sets

    +
  • + +
diff --git a/settings/settings.css b/settings/settings.css index 22a7ea9..dc6c846 100644 --- a/settings/settings.css +++ b/settings/settings.css @@ -8,19 +8,21 @@ body { list-style-type: none; margin-top: 0; padding: 0; - padding-left: 2em; - } -.block-set { - border-bottom-style: solid; - border-bottom-width: 1px; +#block-set-list > li { + border-top-style: solid; + border-top-width: 1px; + padding-top: 1em; + padding-bottom: 1em; } -.block-set:last-child { +/* +#block-set-list > li:last-child { border-bottom-style: none; border-bottom-width: 0; } +*/ .form-group { display: block; @@ -47,7 +49,7 @@ body { } .form-group li:first-child { - margin-top:0; + margin-top:0; } .form-group > label:first-child { @@ -61,6 +63,13 @@ fieldset legend { padding: 0; } +#new-block-set { + margin: auto; + width: 100%; + font-weight: bold; + font-size: large; +} + [aria-invalid="true"] { box-shadow: red 0px 0px 1.5px 1px } diff --git a/settings/settings.js b/settings/settings.js index da547fe..06147c8 100644 --- a/settings/settings.js +++ b/settings/settings.js @@ -1,12 +1,13 @@ 'use strict' -/* globals browser, location */ +/* globals browser, location, Event */ const { getState, setState, formatISOTime, - exampleState + exampleState, + emptyBlockSetSettings } = require('../lib/state.js') const moment = require('moment') const {matchPatternToRegExp} = require('../lib/match-pattern.js') @@ -42,7 +43,7 @@ function expandTemplateChild (template) { console.assert(template.content.children.length === 1) const child = template.content.children[0].cloneNode(true) if (template.parentNode != null) { - template.parentNode.insertBefore(child, template.nextSibling) + template.parentNode.insertBefore(child, template) } return child } @@ -94,7 +95,8 @@ function fillBlockSet (blockSetNode, blockSetSettings) { const editBlockedPageTemplate = node.querySelector('#edit-blocked-page-template') - function addBlockedUrlPattern (pattern) { + + function appendBlockedUrlPattern (pattern) { const container = expandTemplateChild(editBlockedPageTemplate) const input = container.querySelector('input') input.value = pattern @@ -110,6 +112,12 @@ function fillBlockSet (blockSetNode, blockSetSettings) { container.parentNode.removeChild(container) } }) + return container + } + function prependBlockedUrlPattern (pattern) { + const container = appendBlockedUrlPattern(pattern) + container.parentNode.insertBefore(container, container.parentNode.firstChild) + return container } const newBlockedPage = node.querySelector('#new-blocked-page') @@ -120,14 +128,15 @@ function fillBlockSet (blockSetNode, blockSetSettings) { newBlockedPage.parse() != null && newBlockedPage.parse() !== '' ) { - addBlockedUrlPattern(newBlockedPage.parse()) + const container = prependBlockedUrlPattern(newBlockedPage.parse()) + container.parentNode.insertBefore(container, newBlockedPage.parentNode.nextSibling) settings.blockedUrlPatterns.unshift(newBlockedPage.parse()) newBlockedPage.value = '' } }) - for (const pattern of [...blockSetSettings.blockedUrlPatterns].reverse()) { - addBlockedUrlPattern(pattern) + for (const pattern of blockSetSettings.blockedUrlPatterns) { + appendBlockedUrlPattern(pattern) } const startTime = node.querySelector('#start-time') @@ -215,15 +224,25 @@ function fillBlockSet (blockSetNode, blockSetSettings) { } }) } + + setIdPostfix(settings.id, node) } function fillBody (body, state) { const blockSetTemplate = body.querySelector('#block-set-template') - for (const blockSetSettings of [...state.blockSetSettings].reverse()) { + for (const blockSetSettings of state.blockSetSettings) { const blockSetNode = expandTemplateChild(blockSetTemplate) fillBlockSet(blockSetNode, blockSetSettings) - setIdPostfix(blockSetSettings.id, blockSetNode) } + + const newBlockSet = body.querySelector('#new-block-set') + newBlockSet.addEventListener('click', () => { + const blockSetNode = expandTemplateChild(blockSetTemplate) + const settings = emptyBlockSetSettings() + fillBlockSet(blockSetNode, settings) + state.blockSetSettings.push(settings) + newBlockSet.dispatchEvent(new Event('change', {bubbles: true})) + }) } const settingsKeys = ['blockSetSettings']