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 + + + + + + + +
+ LeechBlock logo +
+ + +
+

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

+
+ + +
+ () +

+ The page will never be unblocked. +

+

+ The page will be unblocked at . +

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

&pageAccessBlocked1;&leechblock;&pageAccessBlocked2;

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

&pageUnblockTime;&nullUnblockTime;

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

Freedom logo

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

&pageAccessBlocked1;&leechblock;&pageAccessBlocked2;

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

&pageUnblockTime;&nullUnblockTime;

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

Freedom logo

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

&pageAccessBlocked1;&leechblock;&pageAccessBlocked2;

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

&pageUnblockTime;&nullUnblockTime;

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

&pageAccessDelayed1;&leechblock;&pageAccessDelayed2;

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

&pageWillBeLoaded1;&pageWillBeLoaded2;

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

Freedom logo

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

&pageAccessDelayed1;&leechblock;&pageAccessDelayed2;

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

&pageWillBeLoaded1;&pageWillBeLoaded2;

-
- - -
&considerSupport1;
&considerSupport2;&considerSupport3;&considerSupport4;
- - - - diff --git a/chrome/content/fonts.css b/chrome/content/fonts.css deleted file mode 100644 index 5858fa8..0000000 --- a/chrome/content/fonts.css +++ /dev/null @@ -1,47 +0,0 @@ -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url('fonts/Open-Sans-Light.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 300; - src: local('Open Sans Light'), local('OpenSans-Light'), url('fonts/Open-Sans-Light-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url('fonts/Open-Sans.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -} - -@font-face { - font-family: 'Open Sans'; - font-style: normal; - font-weight: 400; - src: local('Open Sans'), local('OpenSans'), url('fonts/Open-Sans-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: local('Open Sans Italic'), local('OpenSans-Italic'), url('fonts/Open-Sans-Italic.woff2') format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; -} - -@font-face { - font-family: 'Open Sans'; - font-style: italic; - font-weight: 400; - src: local('Open Sans Italic'), local('OpenSans-Italic'), url('fonts/Open-Sans-Italic-ext.woff2') format('woff2'); - unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; -} diff --git a/chrome/content/fonts/LICENSE b/chrome/content/fonts/LICENSE deleted file mode 100644 index d645695..0000000 --- a/chrome/content/fonts/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/chrome/content/fonts/Open-Sans-Italic-ext.woff2 b/chrome/content/fonts/Open-Sans-Italic-ext.woff2 deleted file mode 100644 index 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.whatBlockDesc; - - - &options.customSetName; - - diff --git a/chrome/content/stats.js b/chrome/content/stats.js deleted file mode 100644 index cb1356c..0000000 --- a/chrome/content/stats.js +++ /dev/null @@ -1,107 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* - * This file contains the code for the Statistics dialog. - */ - -// Refreshes values in statistics dialog -// -LeechBlock.statsRefresh = function () { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - for (let set = 1; set <= 6; set++) { - // Get preferences for this set - let setName = LeechBlock.getUniCharPref("setName" + set); - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - let limitMins = LeechBlock.getCharPref("limitMins" + set); - let limitPeriod = LeechBlock.getCharPref("limitPeriod" + set); - let periodStart = LeechBlock.getTimePeriodStart(now, limitPeriod); - - // Update block set name - if (setName == "") { - setName = LeechBlock.locale_blockSet + " " + set; - } - document.getElementById("lb-set-name" + set).value = setName; - - // Update time values - if (timedata.length == 5) { - let fs = LeechBlock.getFormattedStats(timedata); - document.getElementById("lb-start-time" + set).value = fs.startTime; - document.getElementById("lb-total-time" + set).value = fs.totalTime; - document.getElementById("lb-per-day-time" + set).value = fs.perDayTime; - - if (limitMins != "" && limitPeriod != "") { - // Calculate total seconds left in this time period - let secsLeft = timedata[2] == periodStart - ? Math.max(0, (limitMins * 60) - timedata[3]) - : (limitMins * 60); - let timeLeft = LeechBlock.formatTime(secsLeft); - document.getElementById("lb-time-left" + set).value = timeLeft; - } - } - } -} - -// Restarts data gathering for block set -// -LeechBlock.statsRestart = function (set) { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - // Update time data for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[0] = now; - timedata[1] = 0; - } else { - timedata = [now, 0, 0, 0, 0]; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - - // Update display for this set - let fs = LeechBlock.getFormattedStats(timedata); - document.getElementById("lb-start-time" + set).value = fs.startTime; - document.getElementById("lb-total-time" + set).value = fs.totalTime; - document.getElementById("lb-per-day-time" + set).value = fs.perDayTime; -} - -// Restarts data gathering for all block sets -// -LeechBlock.statsRestartAll = function () { - // Get current time in seconds - let now = Math.floor(Date.now() / 1000); - - for (let set = 1; set <= 6; set++) { - // Update time data for this set - let timedata = LeechBlock.getCharPref("timedata" + set).split(","); - if (timedata.length == 5) { - timedata[0] = now; - timedata[1] = 0; - } else { - timedata = [now, 0, 0, 0, 0]; - } - LeechBlock.setCharPref("timedata" + set, timedata.join(",")); - - // Update display for this set - let fs = LeechBlock.getFormattedStats(timedata); - document.getElementById("lb-start-time" + set).value = fs.startTime; - document.getElementById("lb-total-time" + set).value = fs.totalTime; - document.getElementById("lb-per-day-time" + set).value = fs.perDayTime; - } -} - -// Returns formatted times based on time data -// -LeechBlock.getFormattedStats = function (timedata) { - let days = 1 - + Math.floor(Date.now() / 86400000) - - Math.floor(timedata[0] / 86400); - return { - startTime: new Date(timedata[0] * 1000).toLocaleString(), - totalTime: LeechBlock.formatTime(timedata[1]), - perDayTime: LeechBlock.formatTime(timedata[1] / days) - }; -} diff --git a/chrome/content/stats.xul b/chrome/content/stats.xul deleted file mode 100644 index 860fd80..0000000 --- a/chrome/content/stats.xul +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/content/style.css b/chrome/content/style.css deleted file mode 100644 index 2f7b0e2..0000000 --- a/chrome/content/style.css +++ /dev/null @@ -1,41 +0,0 @@ -/* LeechBlock CSS style */ - -body { - background-color: #fff; - color: #2e3436; - font: normal 16px "Open Sans", Arial, sans-serif; - text-align: center; -} - -div { - margin-top: 50px; -} - -h1 { - font-size: 24px; - font-weight: 300; -} - -a { - color: #2ca089; - text-decoration: none; -} - -a:hover { - text-decoration: underline; -} - -.freedom { - font-size: 14px; -} - -.support { - color: #777; - font-size: 14px; - font-style: italic; -} - -.support a { - color: #777; - text-decoration: underline; -} diff --git a/chrome/locale/cs-CZ/leechblock.dtd b/chrome/locale/cs-CZ/leechblock.dtd deleted file mode 100644 index ed489f9..0000000 --- a/chrome/locale/cs-CZ/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/cs-CZ/leechblock.properties b/chrome/locale/cs-CZ/leechblock.properties deleted file mode 100644 index a7c6501..0000000 --- a/chrome/locale/cs-CZ/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Zablokuje webové stránky, které zbytečně odvádějí vaši pozornost od práce, když se to nejméně hodí. diff --git a/chrome/locale/en-US/leechblock.dtd b/chrome/locale/en-US/leechblock.dtd deleted file mode 100644 index 7f69ab2..0000000 --- a/chrome/locale/en-US/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/en-US/leechblock.properties b/chrome/locale/en-US/leechblock.properties deleted file mode 100644 index 1aa614a..0000000 --- a/chrome/locale/en-US/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Blocks those time-wasting sites that can suck the life out of your working day. diff --git a/chrome/locale/fr-FR/leechblock.dtd b/chrome/locale/fr-FR/leechblock.dtd deleted file mode 100644 index 5c13ca0..0000000 --- a/chrome/locale/fr-FR/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/fr-FR/leechblock.properties b/chrome/locale/fr-FR/leechblock.properties deleted file mode 100644 index 3fbe3e0..0000000 --- a/chrome/locale/fr-FR/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Bloque tous ces sites chronophages qui ruinent votre productivité. diff --git a/chrome/locale/pt-BR/leechblock.dtd b/chrome/locale/pt-BR/leechblock.dtd deleted file mode 100644 index 53950dc..0000000 --- a/chrome/locale/pt-BR/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/pt-BR/leechblock.properties b/chrome/locale/pt-BR/leechblock.properties deleted file mode 100644 index 3d82d79..0000000 --- a/chrome/locale/pt-BR/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=Bloqueia aqueles sites que fazem você gastar o tempo útil do seu dia. diff --git a/chrome/locale/zh-CN/leechblock.dtd b/chrome/locale/zh-CN/leechblock.dtd deleted file mode 100644 index 211d0e9..0000000 --- a/chrome/locale/zh-CN/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/zh-CN/leechblock.properties b/chrome/locale/zh-CN/leechblock.properties deleted file mode 100644 index 8f9b790..0000000 --- a/chrome/locale/zh-CN/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=阻止那些浪费时间的网站在你的工作日。 diff --git a/chrome/locale/zh-TW/leechblock.dtd b/chrome/locale/zh-TW/leechblock.dtd deleted file mode 100644 index 02f3177..0000000 --- a/chrome/locale/zh-TW/leechblock.dtd +++ /dev/null @@ -1,160 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/chrome/locale/zh-TW/leechblock.properties b/chrome/locale/zh-TW/leechblock.properties deleted file mode 100644 index ba9a96d..0000000 --- a/chrome/locale/zh-TW/leechblock.properties +++ /dev/null @@ -1 +0,0 @@ -extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description=封鎖浪費工作日生命的網站。 diff --git a/chrome/skin/toolbar.css b/chrome/skin/toolbar.css deleted file mode 100644 index 3933e23..0000000 --- a/chrome/skin/toolbar.css +++ /dev/null @@ -1,17 +0,0 @@ -#leechblock-toolbar-button { - list-style-image: url("chrome://leechblock/skin/leechblock24.png"); -} - -toolbar[iconsize="small"] #leechblock-toolbar-button { - list-style-image: url("chrome://leechblock/skin/leechblock16.png"); -} - -#leechblock-time-left { - background-color: #bbb; - border: 1px solid #808080; - border-radius: 4px; - color: #444; - font: normal 12px "Lucida Console", "Monaco", monospace; - margin: 0px; - padding: 4px 4px 2px 4px; -} diff --git a/defaults/preferences/leechblock.js b/defaults/preferences/leechblock.js deleted file mode 100644 index f41d8c8..0000000 --- a/defaults/preferences/leechblock.js +++ /dev/null @@ -1,188 +0,0 @@ -pref("extensions.{a95d8332-e4b4-6e7f-98ac-20b733364387}.description", "chrome://leechblock/locale/leechblock.properties"); -pref("extensions.leechblock.setName1", ""); -pref("extensions.leechblock.setName2", ""); -pref("extensions.leechblock.setName3", ""); -pref("extensions.leechblock.setName4", ""); -pref("extensions.leechblock.setName5", ""); -pref("extensions.leechblock.setName6", ""); -pref("extensions.leechblock.sites1", ""); -pref("extensions.leechblock.sites2", ""); -pref("extensions.leechblock.sites3", ""); -pref("extensions.leechblock.sites4", ""); -pref("extensions.leechblock.sites5", ""); -pref("extensions.leechblock.sites6", ""); -pref("extensions.leechblock.sitesURL1", ""); -pref("extensions.leechblock.sitesURL2", ""); -pref("extensions.leechblock.sitesURL3", ""); -pref("extensions.leechblock.sitesURL4", ""); -pref("extensions.leechblock.sitesURL5", ""); -pref("extensions.leechblock.sitesURL6", ""); -pref("extensions.leechblock.blockRE1", ""); -pref("extensions.leechblock.blockRE2", ""); -pref("extensions.leechblock.blockRE3", ""); -pref("extensions.leechblock.blockRE4", ""); -pref("extensions.leechblock.blockRE5", ""); -pref("extensions.leechblock.blockRE6", ""); -pref("extensions.leechblock.allowRE1", ""); -pref("extensions.leechblock.allowRE2", ""); -pref("extensions.leechblock.allowRE3", ""); -pref("extensions.leechblock.allowRE4", ""); -pref("extensions.leechblock.allowRE5", ""); -pref("extensions.leechblock.allowRE6", ""); -pref("extensions.leechblock.keywordRE1", ""); -pref("extensions.leechblock.keywordRE2", ""); -pref("extensions.leechblock.keywordRE3", ""); -pref("extensions.leechblock.keywordRE4", ""); -pref("extensions.leechblock.keywordRE5", ""); -pref("extensions.leechblock.keywordRE6", ""); -pref("extensions.leechblock.times1", "0900-1700"); -pref("extensions.leechblock.times2", "0900-1700"); -pref("extensions.leechblock.times3", "0900-1700"); -pref("extensions.leechblock.times4", "0900-1700"); -pref("extensions.leechblock.times5", "0900-1700"); -pref("extensions.leechblock.times6", "0900-1700"); -pref("extensions.leechblock.limitMins1", ""); -pref("extensions.leechblock.limitMins2", ""); -pref("extensions.leechblock.limitMins3", ""); -pref("extensions.leechblock.limitMins4", ""); -pref("extensions.leechblock.limitMins5", ""); -pref("extensions.leechblock.limitMins6", ""); -pref("extensions.leechblock.limitPeriod1", ""); -pref("extensions.leechblock.limitPeriod2", ""); -pref("extensions.leechblock.limitPeriod3", ""); -pref("extensions.leechblock.limitPeriod4", ""); -pref("extensions.leechblock.limitPeriod5", ""); -pref("extensions.leechblock.limitPeriod6", ""); -pref("extensions.leechblock.conjMode", 0); -pref("extensions.leechblock.days1", 62); -pref("extensions.leechblock.days2", 62); -pref("extensions.leechblock.days3", 62); -pref("extensions.leechblock.days4", 62); -pref("extensions.leechblock.days5", 62); -pref("extensions.leechblock.days6", 62); -pref("extensions.leechblock.blockURL1", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL2", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL3", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL4", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL5", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.blockURL6", "chrome://leechblock/content/blocked.xhtml?$S&$U"); -pref("extensions.leechblock.activeBlock", 0); -pref("extensions.leechblock.countFocus", 63); -pref("extensions.leechblock.delayFirst", 63); -pref("extensions.leechblock.delaySecs1", "60"); -pref("extensions.leechblock.delaySecs2", "60"); -pref("extensions.leechblock.delaySecs3", "60"); -pref("extensions.leechblock.delaySecs4", "60"); -pref("extensions.leechblock.delaySecs5", "60"); -pref("extensions.leechblock.delaySecs6", "60"); -pref("extensions.leechblock.prevOpts", 0); -pref("extensions.leechblock.prevAddons", 0); -pref("extensions.leechblock.prevConfig", 0); -pref("extensions.leechblock.oa", 0); -pref("extensions.leechblock.hpp", true); -pref("extensions.leechblock.ham", false); -pref("extensions.leechblock.hsm", false); -pref("extensions.leechblock.warnSecs", ""); -pref("extensions.leechblock.bep", false); -pref("extensions.leechblock.kpb", false); -pref("extensions.leechblock.hcm", false); -pref("extensions.leechblock.htl", false); -pref("extensions.leechblock.timedata1", ""); -pref("extensions.leechblock.timedata2", ""); -pref("extensions.leechblock.timedata3", ""); -pref("extensions.leechblock.timedata4", ""); -pref("extensions.leechblock.timedata5", ""); -pref("extensions.leechblock.timedata6", ""); -pref("extensions.leechblock.repeatCheckPeriod", 5000); -pref("extensions.leechblock.lockdownDuration", 0); -pref("extensions.leechblock.lockdownSets", 0); -pref("extensions.leechblock.ao", ""); -pref("extensions.leechblock.ap", ""); -pref("services.sync.prefs.sync.extensions.leechblock.setName1", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName2", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName3", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName4", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName5", true); -pref("services.sync.prefs.sync.extensions.leechblock.setName6", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites1", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites2", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites3", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites4", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites5", true); -pref("services.sync.prefs.sync.extensions.leechblock.sites6", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL1", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL2", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL3", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL4", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL5", true); -pref("services.sync.prefs.sync.extensions.leechblock.sitesURL6", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE1", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE2", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE3", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE4", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE5", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockRE6", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE1", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE2", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE3", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE4", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE5", true); -pref("services.sync.prefs.sync.extensions.leechblock.allowRE6", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE1", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE2", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE3", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE4", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE5", true); -pref("services.sync.prefs.sync.extensions.leechblock.keywordRE6", true); -pref("services.sync.prefs.sync.extensions.leechblock.times1", true); -pref("services.sync.prefs.sync.extensions.leechblock.times2", true); -pref("services.sync.prefs.sync.extensions.leechblock.times3", true); -pref("services.sync.prefs.sync.extensions.leechblock.times4", true); -pref("services.sync.prefs.sync.extensions.leechblock.times5", true); -pref("services.sync.prefs.sync.extensions.leechblock.times6", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins1", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins2", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins3", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins4", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins5", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitMins6", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod1", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod2", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod3", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod4", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod5", true); -pref("services.sync.prefs.sync.extensions.leechblock.limitPeriod6", true); -pref("services.sync.prefs.sync.extensions.leechblock.conjMode", true); -pref("services.sync.prefs.sync.extensions.leechblock.days1", true); -pref("services.sync.prefs.sync.extensions.leechblock.days2", true); -pref("services.sync.prefs.sync.extensions.leechblock.days3", true); -pref("services.sync.prefs.sync.extensions.leechblock.days4", true); -pref("services.sync.prefs.sync.extensions.leechblock.days5", true); -pref("services.sync.prefs.sync.extensions.leechblock.days6", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL1", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL2", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL3", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL4", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL5", true); -pref("services.sync.prefs.sync.extensions.leechblock.blockURL6", true); -pref("services.sync.prefs.sync.extensions.leechblock.activeBlock", true); -pref("services.sync.prefs.sync.extensions.leechblock.countFocus", true); -pref("services.sync.prefs.sync.extensions.leechblock.delayFirst", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs1", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs2", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs3", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs4", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs5", true); -pref("services.sync.prefs.sync.extensions.leechblock.delaySecs6", true); -pref("services.sync.prefs.sync.extensions.leechblock.prevOpts", true); -pref("services.sync.prefs.sync.extensions.leechblock.prevAddons", true); -pref("services.sync.prefs.sync.extensions.leechblock.prevConfig", true); -pref("services.sync.prefs.sync.extensions.leechblock.oa", true); -pref("services.sync.prefs.sync.extensions.leechblock.hpp", true); -pref("services.sync.prefs.sync.extensions.leechblock.ham", true); -pref("services.sync.prefs.sync.extensions.leechblock.hsm", true); -pref("services.sync.prefs.sync.extensions.leechblock.warnSecs", true); -pref("services.sync.prefs.sync.extensions.leechblock.bep", true); -pref("services.sync.prefs.sync.extensions.leechblock.kpb", true); -pref("services.sync.prefs.sync.extensions.leechblock.hcm", true); -pref("services.sync.prefs.sync.extensions.leechblock.htl", true); diff --git a/install.rdf b/install.rdf deleted file mode 100644 index c225adb..0000000 --- a/install.rdf +++ /dev/null @@ -1,33 +0,0 @@ - - - - - {a95d8332-e4b4-6e7f-98ac-20b733364387} - 1.0.8 - 2 - true - LeechBlock - Blocks those time-wasting sites that can suck the life out of your working day. - James Anderson - Michal Stanke (cs-CZ) - ioreker (fr-FR) - Guillaume Bignon (fr-FR) - Gutierrez PS (pt-BR) [http://about.me/gutierrezps] - Wang.H.K (zh-CN) - Rex Tsai (zh-TW) - Allen Chen (zh-CN & zh-TW) - Dario Kolak - chrome://leechblock/content/options.xul - chrome://leechblock/skin/leechblock32.png - chrome://leechblock/skin/leechblock64.png - http://www.proginosko.com/leechblock/ - - - {ec8030f7-c20a-464f-9b0e-13a3a9e97384} - 38.0 - 54.* - - - - diff --git a/lib/logic.js b/lib/logic.js new file mode 100644 index 0000000..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() + ) + }) +})