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..d85ac12
--- /dev/null
+++ b/background.js
@@ -0,0 +1,124 @@
+'use strict'
+/* globals browser */
+
+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 != null) {
+ 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 != null))
+}
+
+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 += '#'
+ const unblockingMoment = nextNonBlockingMoment(now, settings, data)
+ if (unblockingMoment != null) {
+ 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 != null && 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 == null) {
+ 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..c6000b1
--- /dev/null
+++ b/block/block.js
@@ -0,0 +1,66 @@
+'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 != null) {
+ blockedUrlLink.appendChild(document.createTextNode(blockedUrl))
+ blockedUrlLink.setAttribute('href', blockedUrl)
+} else {
+ blockedUrlLink.style.display = 'none'
+}
+
+if (unblockingDate != null) {
+ const now = new Date()
+ if (unblockingDate < now && blockedUrl != null) {
+ 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'
+}
+
+if (blockSetName != null) {
+ 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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/chrome/content/blocked+reload.xhtml b/chrome/content/blocked+reload.xhtml
deleted file mode 100644
index 6190228..0000000
--- a/chrome/content/blocked+reload.xhtml
+++ /dev/null
@@ -1,53 +0,0 @@
-
- %xhtmlDTD;
-
- %leechblockDTD;
-]>
-
-
-
-
- &siteBlocked;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (
&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.
-
-
-
-
-
-
-
-
-
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;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (
&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.
-
-
-
-
-
-
-
-
-
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;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (
&blockSet; )
-
&pageUnblockTime;&nullUnblockTime;
-
-
-
-
-
-
-
-
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;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (
&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.
-
-
-
-
-
-
-
-
-
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;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (
&blockSet; )
-
&pageWillBeLoaded1; &pageWillBeLoaded2;
-
-
-
-
-
-
-
-
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 72208f3..0000000
Binary files a/chrome/content/fonts/Open-Sans-Italic-ext.woff2 and /dev/null differ
diff --git a/chrome/content/fonts/Open-Sans-Italic.woff2 b/chrome/content/fonts/Open-Sans-Italic.woff2
deleted file mode 100644
index a739e6a..0000000
Binary files a/chrome/content/fonts/Open-Sans-Italic.woff2 and /dev/null differ
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 3e198c1..0000000
Binary files a/chrome/content/fonts/Open-Sans-Light-ext.woff2 and /dev/null differ
diff --git a/chrome/content/fonts/Open-Sans-Light.woff2 b/chrome/content/fonts/Open-Sans-Light.woff2
deleted file mode 100644
index 96ae7ed..0000000
Binary files a/chrome/content/fonts/Open-Sans-Light.woff2 and /dev/null differ
diff --git a/chrome/content/fonts/Open-Sans-ext.woff2 b/chrome/content/fonts/Open-Sans-ext.woff2
deleted file mode 100644
index 0b5a66e..0000000
Binary files a/chrome/content/fonts/Open-Sans-ext.woff2 and /dev/null differ
diff --git a/chrome/content/fonts/Open-Sans.woff2 b/chrome/content/fonts/Open-Sans.woff2
deleted file mode 100644
index 5287058..0000000
Binary files a/chrome/content/fonts/Open-Sans.woff2 and /dev/null differ
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.customSetName;
-
-
-
-
- &options.sitesDesc;
-
-
- &options.sitesLoadFromURL;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.timesDesc;
-
-
-
-
-
-
- &options.timeLimitDesc;
-
- &options.minsInEveryDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.modeDesc;
-
-
-
-
-
-
-
-
-
- &options.daysDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.blockDesc;
-
-
-
-
- &options.predefinedURLs;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.delaySecsDesc1;
-
- &options.delaySecsDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.advancedWarnDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.customSetName;
-
-
-
-
- &options.sitesDesc;
-
-
- &options.sitesLoadFromURL;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.timesDesc;
-
-
-
-
-
-
- &options.timeLimitDesc;
-
- &options.minsInEveryDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.modeDesc;
-
-
-
-
-
-
-
-
-
- &options.daysDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.blockDesc;
-
-
-
-
- &options.predefinedURLs;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.delaySecsDesc1;
-
- &options.delaySecsDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.advancedWarnDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.customSetName;
-
-
-
-
- &options.sitesDesc;
-
-
- &options.sitesLoadFromURL;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.timesDesc;
-
-
-
-
-
-
- &options.timeLimitDesc;
-
- &options.minsInEveryDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.modeDesc;
-
-
-
-
-
-
-
-
-
- &options.daysDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.blockDesc;
-
-
-
-
- &options.predefinedURLs;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.delaySecsDesc1;
-
- &options.delaySecsDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.advancedWarnDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.customSetName;
-
-
-
-
- &options.sitesDesc;
-
-
- &options.sitesLoadFromURL;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.timesDesc;
-
-
-
-
-
-
- &options.timeLimitDesc;
-
- &options.minsInEveryDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.modeDesc;
-
-
-
-
-
-
-
-
-
- &options.daysDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.blockDesc;
-
-
-
-
- &options.predefinedURLs;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.delaySecsDesc1;
-
- &options.delaySecsDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.advancedWarnDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.customSetName;
-
-
-
-
- &options.sitesDesc;
-
-
- &options.sitesLoadFromURL;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.timesDesc;
-
-
-
-
-
-
- &options.timeLimitDesc;
-
- &options.minsInEveryDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.modeDesc;
-
-
-
-
-
-
-
-
-
- &options.daysDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.blockDesc;
-
-
-
-
- &options.predefinedURLs;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.delaySecsDesc1;
-
- &options.delaySecsDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.advancedWarnDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.customSetName;
-
-
-
-
- &options.sitesDesc;
-
-
- &options.sitesLoadFromURL;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.timesDesc;
-
-
-
-
-
-
- &options.timeLimitDesc;
-
- &options.minsInEveryDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.modeDesc;
-
-
-
-
-
-
-
-
-
- &options.daysDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.blockDesc;
-
-
-
-
- &options.predefinedURLs;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.delaySecsDesc1;
-
- &options.delaySecsDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.advancedWarnDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.accessDesc;
-
-
-
-
-
-
-
-
-
-
-
-
- &options.passwordDesc;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- &options.warnMessageDesc1;
-
- &options.warnMessageDesc2;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
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..0f4e58f
--- /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) { // eslint-disable-line no-unmodified-loop-condition
+ result.add(duration)
+ }
+ } else {
+ while (result > now) { // eslint-disable-line no-unmodified-loop-condition
+ 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
+ if (quotaAllowed > moment.duration(0)) {
+ nextReset = nextIntervalBoundary(now, quotaReset, quotaInterval)
+ }
+
+ if (nextInactive == null) {
+ return nextReset
+ }
+ if (nextReset == null) {
+ 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 != null && 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..6f7200a
--- /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..c6f1dcb
--- /dev/null
+++ b/lib/state.js
@@ -0,0 +1,188 @@
+'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') {
+ // 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) {
+ const r = Math.random() * 16 | 0
+ const v = c === 'x' ? r : (r & 0x3 | 0x8)
+ return v.toString(16)
+ })
+}
+
+function emptyBlockSetSettings () {
+ 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: [emptyBlockSetSettings()],
+ 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 => {
+ console.log(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,
+ emptyBlockSetSettings,
+ 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..080c60e
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2604 @@
+{
+ "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="
+ },
+ "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",
+ "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="
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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.2",
+ "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-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",
+ "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="
+ },
+ "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",
+ "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=",
+ "requires": {
+ "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",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "requires": {
+ "inherits": "2.0.3",
+ "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",
+ "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="
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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=="
+ },
+ "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.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",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "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",
+ "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-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",
+ "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="
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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="
+ },
+ "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",
+ "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="
+ }
+ }
+ },
+ "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",
+ "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=",
+ "requires": {
+ "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",
+ "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.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",
+ "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": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "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"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ }
+ }
+ },
+ "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"
+ },
+ "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": {
+ "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="
+ },
+ "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",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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="
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=",
+ "requires": {
+ "hash-base": "2.0.2",
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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-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",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "requires": {
+ "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",
+ "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"
+ }
+ },
+ "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"
+ }
+ },
+ "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",
+ "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="
+ },
+ "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",
+ "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="
+ }
+ }
+ },
+ "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",
+ "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="
+ }
+ }
+ },
+ "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",
+ "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"
+ }
+ },
+ "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",
+ "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..2e2dd41
--- /dev/null
+++ b/package.json
@@ -0,0 +1,14 @@
+{
+ "dependencies": [
+ "moment"
+ ],
+ "devDependencies": [
+ "mocha",
+ "browserify",
+ "jsdoc",
+ "standard"
+ ],
+ "scripts": {
+ "test": "mocha"
+ }
+}
diff --git a/settings/en_US.html b/settings/en_US.html
new file mode 100644
index 0000000..751db26
--- /dev/null
+++ b/settings/en_US.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+ Block Sets
+
+
+
+
+
+
diff --git a/settings/settings.css b/settings/settings.css
new file mode 100644
index 0000000..dc6c846
--- /dev/null
+++ b/settings/settings.css
@@ -0,0 +1,86 @@
+body {
+ max-width: 800px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+#block-set-list {
+ list-style-type: none;
+ margin-top: 0;
+ padding: 0;
+}
+
+#block-set-list > li {
+ border-top-style: solid;
+ border-top-width: 1px;
+ padding-top: 1em;
+ padding-bottom: 1em;
+}
+
+/*
+#block-set-list > li: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;
+}
+
+#new-block-set {
+ margin: auto;
+ width: 100%;
+ font-weight: bold;
+ font-size: large;
+}
+
+[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..06147c8
--- /dev/null
+++ b/settings/settings.js
@@ -0,0 +1,288 @@
+'use strict'
+
+/* globals browser, location, Event */
+
+const {
+ getState,
+ setState,
+ formatISOTime,
+ exampleState,
+ emptyBlockSetSettings
+} = 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 != null) {
+ node.removeAttribute('aria-invalid')
+ } else {
+ node.setAttribute('aria-invalid', true)
+ }
+ return parsedValue
+ }
+}
+
+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)
+ }
+ return child
+}
+
+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) != null) {
+ 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 appendBlockedUrlPattern (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() != null) {
+ settings.blockedUrlPatterns[i] = input.parse()
+ }
+ if (input.parse() === '') {
+ settings.blockedUrlPatterns.splice(i, 1)
+ 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')
+ newBlockedPage.value = ''
+ setParse(newBlockedPage, parseMatchPattern0)
+ newBlockedPage.addEventListener('change', () => {
+ if (
+ newBlockedPage.parse() != null &&
+ 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) {
+ appendBlockedUrlPattern(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() != null && endTime.parse() != null) {
+ 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 != null) {
+ 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 != null) {
+ const unit = quotaIntervalUnit.value
+ settings.quotaInterval = moment.duration(number, unit).toISOString()
+ }
+ })
+ }
+
+ setIdPostfix(settings.id, node)
+}
+
+function fillBody (body, state) {
+ const blockSetTemplate = body.querySelector('#block-set-template')
+ for (const blockSetSettings of state.blockSetSettings) {
+ const blockSetNode = expandTemplateChild(blockSetTemplate)
+ fillBlockSet(blockSetNode, blockSetSettings)
+ }
+
+ 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']
+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..a1a1e7a
--- /dev/null
+++ b/test/logic.js
@@ -0,0 +1,239 @@
+'use strict'
+/* globals describe, it */
+
+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()
+ )
+ })
+})