From bdd0df129605bf73f7b14ab76a3c048c58157558 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:45:01 -0400 Subject: [PATCH 01/77] Added new vimium.js with code taken from vimium --- webext/src/vimium.js | 825 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 825 insertions(+) create mode 100644 webext/src/vimium.js diff --git a/webext/src/vimium.js b/webext/src/vimium.js new file mode 100644 index 00000000..6463294f --- /dev/null +++ b/webext/src/vimium.js @@ -0,0 +1,825 @@ +// The code in this file was copied and adapted from vimium's codebase. +// It's MIT licensed, so there should be no problem mixing it with +// Browsh's codebase or even relicensing the code as LGPLv2. +// +// Here is the list of changes made to vimium's original code in order +// to better fullfill the needs of Browsh: +// +// In line 11 of getLocalHints the following code was added: +// +// if (requireHref && element.href && visibleElement.length > 0) { +// visibleElement[0]["href"] = element.href; +// } +// +// Also in getLocalHints the following outcommented code was replaced +// with the code preceding it in order to prevent Firefox from crashing: +// +// try { +// var testextend = extend(visibleElement, { rect: rects[0] }); +// nonOverlappingElements.push(testextend); +// } catch(error) { +// nonOverlappingElements.push(visibleElement); +// } +// /*nonOverlappingElements.push(extend(visibleElement, { +// rect: rects[0] +// }));*/ +// +// The following lines just before the return statement in getLocalHints were +// commented out, because we're currently not using this functionality and the +// settings for it simply don't exist in Browsh (yet). +// +// /*if (Settings.get("filterLinkHints")) { +// for (m = 0, len3 = localHints.length; m < len3; m++) { +// hint = localHints[m]; +// extend(hint, this.generateLinkText(hint)); +// } +// }*/ +// +// In multiple places Utils.isFirefox() calls were commented out or replaced with true, +// because Browsh is currently assuming that it is being run in Firefox. + +var Rect = { + create: function(x1, y1, x2, y2) { + return { + bottom: y2, + top: y1, + left: x1, + right: x2, + width: x2 - x1, + height: y2 - y1 + }; + }, + copy: function(rect) { + return { + bottom: rect.bottom, + top: rect.top, + left: rect.left, + right: rect.right, + width: rect.width, + height: rect.height + }; + }, + translate: function(rect, x, y) { + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + return { + bottom: rect.bottom + y, + top: rect.top + y, + left: rect.left + x, + right: rect.right + x, + width: rect.width, + height: rect.height + }; + }, + subtract: function(rect1, rect2) { + var rects; + rect2 = this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); + if (rect2.width < 0 || rect2.height < 0) { + return [Rect.copy(rect1)]; + } + rects = [this.create(rect1.left, rect1.top, rect2.left, rect2.top), this.create(rect2.left, rect1.top, rect2.right, rect2.top), this.create(rect2.right, rect1.top, rect1.right, rect2.top), this.create(rect1.left, rect2.top, rect2.left, rect2.bottom), this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom)]; + return rects.filter(function(rect) { + return rect.height > 0 && rect.width > 0; + }); + }, + intersects: function(rect1, rect2) { + return rect1.right > rect2.left && rect1.left < rect2.right && rect1.bottom > rect2.top && rect1.top < rect2.bottom; + }, + intersectsStrict: function(rect1, rect2) { + return rect1.right >= rect2.left && rect1.left <= rect2.right && rect1.bottom >= rect2.top && rect1.top <= rect2.bottom; + }, + equals: function(rect1, rect2) { + var i, len, property, ref; + ref = ["top", "bottom", "left", "right", "width", "height"]; + for (i = 0, len = ref.length; i < len; i++) { + property = ref[i]; + if (rect1[property] !== rect2[property]) { + return false; + } + } + return true; + }, + intersect: function(rect1, rect2) { + return this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); + } + }; + +var DomUtils = { + documentReady: function() { + var callbacks, isReady, onDOMContentLoaded, ref; + ref = [document.readyState !== "loading", []], isReady = ref[0], callbacks = ref[1]; + if (!isReady) { + window.addEventListener("DOMContentLoaded", onDOMContentLoaded = forTrusted(function() { + var callback, i, len; + window.removeEventListener("DOMContentLoaded", onDOMContentLoaded); + isReady = true; + for (i = 0, len = callbacks.length; i < len; i++) { + callback = callbacks[i]; + callback(); + } + return callbacks = null; + })); + } + return function(callback) { + if (isReady) { + return callback(); + } else { + return callbacks.push(callback); + } + }; + }, + getVisibleClientRect: function(element, testChildren) { + var child, childClientRect, clientRect, clientRects, computedStyle, i, isInlineZeroHeight, j, len, len1, ref, ref1; + if (testChildren == null) { + testChildren = false; + } + clientRects = (function() { + var i, len, ref, results; + ref = element.getClientRects(); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + clientRect = ref[i]; + results.push(Rect.copy(clientRect)); + } + return results; + })(); + isInlineZeroHeight = function() { + var elementComputedStyle, isInlineZeroFontSize; + elementComputedStyle = window.getComputedStyle(element, null); + isInlineZeroFontSize = (0 === elementComputedStyle.getPropertyValue("display").indexOf("inline")) && (elementComputedStyle.getPropertyValue("font-size") === "0px"); + isInlineZeroHeight = function() { + return isInlineZeroFontSize; + }; + return isInlineZeroFontSize; + }; + for (i = 0, len = clientRects.length; i < len; i++) { + clientRect = clientRects[i]; + if ((clientRect.width === 0 || clientRect.height === 0) && testChildren) { + ref = element.children; + for (j = 0, len1 = ref.length; j < len1; j++) { + child = ref[j]; + computedStyle = window.getComputedStyle(child, null); + if (computedStyle.getPropertyValue("float") === "none" && !((ref1 = computedStyle.getPropertyValue("position")) === "absolute" || ref1 === "fixed") && !(clientRect.height === 0 && isInlineZeroHeight() && 0 === computedStyle.getPropertyValue("display").indexOf("inline"))) { + continue; + } + childClientRect = this.getVisibleClientRect(child, true); + if (childClientRect === null || childClientRect.width < 3 || childClientRect.height < 3) { + continue; + } + return childClientRect; + } + } else { + clientRect = this.cropRectToVisible(clientRect); + if (clientRect === null || clientRect.width < 3 || clientRect.height < 3) { + continue; + } + computedStyle = window.getComputedStyle(element, null); + if (computedStyle.getPropertyValue('visibility') !== 'visible') { + continue; + } + return clientRect; + } + } + return null; + }, + cropRectToVisible: function(rect) { + var boundedRect; + boundedRect = Rect.create(Math.max(rect.left, 0), Math.max(rect.top, 0), rect.right, rect.bottom); + if (boundedRect.top >= window.innerHeight - 4 || boundedRect.left >= window.innerWidth - 4) { + return null; + } else { + return boundedRect; + } + }, + getClientRectsForAreas: function(imgClientRect, areas) { + var area, coords, diff, i, len, r, rect, rects, ref, shape, x, x1, x2, y, y1, y2; + rects = []; + for (i = 0, len = areas.length; i < len; i++) { + area = areas[i]; + coords = area.coords.split(",").map(function(coord) { + return parseInt(coord, 10); + }); + shape = area.shape.toLowerCase(); + if (shape === "rect" || shape === "rectangle") { + x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; + } else if (shape === "circle" || shape === "circ") { + x = coords[0], y = coords[1], r = coords[2]; + diff = r / Math.sqrt(2); + x1 = x - diff; + x2 = x + diff; + y1 = y - diff; + y2 = y + diff; + } else if (shape === "default") { + ref = [0, 0, imgClientRect.width, imgClientRect.height], x1 = ref[0], y1 = ref[1], x2 = ref[2], y2 = ref[3]; + } else { + x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; + } + rect = Rect.translate(Rect.create(x1, y1, x2, y2), imgClientRect.left, imgClientRect.top); + rect = this.cropRectToVisible(rect); + if (rect && !isNaN(rect.top)) { + rects.push({ + element: area, + rect: rect + }); + } + } + return rects; + }, + isSelectable: function(element) { + var unselectableTypes; + if (!(element instanceof Element)) { + return false; + } + unselectableTypes = ["button", "checkbox", "color", "file", "hidden", "image", "radio", "reset", "submit"]; + return (element.nodeName.toLowerCase() === "input" && unselectableTypes.indexOf(element.type) === -1) || element.nodeName.toLowerCase() === "textarea" || element.isContentEditable; + }, + getViewportTopLeft: function() { + var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; + box = document.documentElement; + style = getComputedStyle(box); + rect = box.getBoundingClientRect(); + if (style.position === "static" && !/content|paint|strict/.test(style.contain || "")) { + marginTop = parseInt(style.marginTop); + marginLeft = parseInt(style.marginLeft); + return { + top: -rect.top + marginTop, + left: -rect.left + marginLeft + }; + } else { + //if (Utils.isFirefox()) + if (true) { + clientTop = parseInt(style.borderTopWidth); + clientLeft = parseInt(style.borderLeftWidth); + } else { + clientTop = box.clientTop, clientLeft = box.clientLeft; + } + return { + top: -rect.top - clientTop, + left: -rect.left - clientLeft + }; + } + }, + makeXPath: function(elementArray) { + var element, i, len, xpath; + xpath = []; + for (i = 0, len = elementArray.length; i < len; i++) { + element = elementArray[i]; + xpath.push(".//" + element, ".//xhtml:" + element); + } + return xpath.join(" | "); + }, + evaluateXPath: function(xpath, resultType) { + var contextNode, namespaceResolver; + contextNode = document.webkitIsFullScreen ? document.webkitFullscreenElement : document.documentElement; + namespaceResolver = function(namespace) { + if (namespace === "xhtml") { + return "http://www.w3.org/1999/xhtml"; + } else { + return null; + } + }; + return document.evaluate(xpath, contextNode, namespaceResolver, resultType, null); + }, + simulateClick: function(element, modifiers) { + var defaultActionShouldTrigger, event, eventSequence, i, len, results; + if (modifiers == null) { + modifiers = {}; + } + eventSequence = ["mouseover", "mousedown", "mouseup", "click"]; + results = []; + for (i = 0, len = eventSequence.length; i < len; i++) { + event = eventSequence[i]; + defaultActionShouldTrigger = /*Utils.isFirefox() &&*/ Object.keys(modifiers).length === 0 && event === "click" && element.target === "_blank" && element.href && !element.hasAttribute("onclick") && !element.hasAttribute("_vimium-has-onclick-listener") ? true : this.simulateMouseEvent(event, element, modifiers); + if (event === "click" && defaultActionShouldTrigger /*&& Utils.isFirefox()*/) { + if (0 < Object.keys(modifiers).length || element.target === "_blank") { + DomUtils.simulateClickDefaultAction(element, modifiers); + } + } + results.push(defaultActionShouldTrigger); + } + return results; + }, + simulateMouseEvent: (function() { + var lastHoveredElement; + lastHoveredElement = void 0; + return function(event, element, modifiers) { + var mouseEvent; + if (modifiers == null) { + modifiers = {}; + } + if (event === "mouseout") { + if (element == null) { + element = lastHoveredElement; + } + lastHoveredElement = void 0; + if (element == null) { + return; + } + } else if (event === "mouseover") { + this.simulateMouseEvent("mouseout", void 0, modifiers); + lastHoveredElement = element; + } + mouseEvent = document.createEvent("MouseEvents"); + mouseEvent.initMouseEvent(event, true, true, window, 1, 0, 0, 0, 0, modifiers.ctrlKey, modifiers.altKey, modifiers.shiftKey, modifiers.metaKey, 0, null); + return element.dispatchEvent(mouseEvent); + }; + })(), + simulateClickDefaultAction: function(element, modifiers) { + var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; + if (modifiers == null) { + modifiers = {}; + } + if (!(((ref = element.tagName) != null ? ref.toLowerCase() : void 0) === "a" && (element.href != null))) { + return; + } + ctrlKey = modifiers.ctrlKey, shiftKey = modifiers.shiftKey, metaKey = modifiers.metaKey, altKey = modifiers.altKey; + if (KeyboardUtils.platform === "Mac") { + newTabModifier = metaKey === true && ctrlKey === false; + } else { + newTabModifier = metaKey === false && ctrlKey === true; + } + if (newTabModifier) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: shiftKey === true + }); + } else if (shiftKey === true && metaKey === false && ctrlKey === false && altKey === false) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewWindow", + url: element.href + }); + } else if (element.target === "_blank") { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: true + }); + } + } +} + + var LocalHints = { + getVisibleClickable: function(element) { + var actionName, areas, areasAndRects, base1, clientRect, contentEditable, eventType, i, imgClientRects, isClickable, jsactionRule, jsactionRules, len, map, mapName, namespace, onlyHasTabIndex, possibleFalsePositive, reason, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, role, ruleSplit, tabIndex, tabIndexValue, tagName, visibleElements, slice; + tagName = (ref = typeof (base1 = element.tagName).toLowerCase === "function" ? base1.toLowerCase() : void 0) != null ? ref : ""; + isClickable = false; + onlyHasTabIndex = false; + possibleFalsePositive = false; + visibleElements = []; + reason = null; + slice = [].slice; + if (tagName === "img") { + mapName = element.getAttribute("usemap"); + if (mapName) { + imgClientRects = element.getClientRects(); + mapName = mapName.replace(/^#/, "").replace("\"", "\\\""); + map = document.querySelector("map[name=\"" + mapName + "\"]"); + if (map && imgClientRects.length > 0) { + areas = map.getElementsByTagName("area"); + areasAndRects = DomUtils.getClientRectsForAreas(imgClientRects[0], areas); + visibleElements.push.apply(visibleElements, areasAndRects); + } + } + } + if (((ref1 = (ref2 = element.getAttribute("aria-hidden")) != null ? ref2.toLowerCase() : void 0) === "" || ref1 === "true") || ((ref3 = (ref4 = element.getAttribute("aria-disabled")) != null ? ref4.toLowerCase() : void 0) === "" || ref3 === "true")) { + return []; + } + if (this.checkForAngularJs == null) { + this.checkForAngularJs = (function() { + var angularElements, i, k, len, len1, ngAttributes, prefix, ref5, ref6, separator; + angularElements = document.getElementsByClassName("ng-scope"); + if (angularElements.length === 0) { + return function() { + return false; + }; + } else { + ngAttributes = []; + ref5 = ['', 'data-', 'x-']; + for (i = 0, len = ref5.length; i < len; i++) { + prefix = ref5[i]; + ref6 = ['-', ':', '_']; + for (k = 0, len1 = ref6.length; k < len1; k++) { + separator = ref6[k]; + ngAttributes.push(prefix + "ng" + separator + "click"); + } + } + return function(element) { + var attribute, l, len2; + for (l = 0, len2 = ngAttributes.length; l < len2; l++) { + attribute = ngAttributes[l]; + if (element.hasAttribute(attribute)) { + return true; + } + } + return false; + }; + } + })(); + } + isClickable || (isClickable = this.checkForAngularJs(element)); + if (element.hasAttribute("onclick") || (role = element.getAttribute("role")) && ((ref5 = role.toLowerCase()) === "button" || ref5 === "tab" || ref5 === "link" || ref5 === "checkbox" || ref5 === "menuitem" || ref5 === "menuitemcheckbox" || ref5 === "menuitemradio") || (contentEditable = element.getAttribute("contentEditable")) && ((ref6 = contentEditable.toLowerCase()) === "" || ref6 === "contenteditable" || ref6 === "true")) { + isClickable = true; + } + if (!isClickable && element.hasAttribute("jsaction")) { + jsactionRules = element.getAttribute("jsaction").split(";"); + for (i = 0, len = jsactionRules.length; i < len; i++) { + jsactionRule = jsactionRules[i]; + ruleSplit = jsactionRule.trim().split(":"); + if ((1 <= (ref7 = ruleSplit.length) && ref7 <= 2)) { + ref8 = ruleSplit.length === 1 ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), ["_"]) : [ruleSplit[0]].concat(slice.call(ruleSplit[1].trim().split(".")), ["_"]), eventType = ref8[0], namespace = ref8[1], actionName = ref8[2]; + isClickable || (isClickable = eventType === "click" && namespace !== "none" && actionName !== "_"); + } + } + } + switch (tagName) { + case "a": + isClickable = true; + break; + case "textarea": + isClickable || (isClickable = !element.disabled && !element.readOnly); + break; + case "input": + isClickable || (isClickable = !(((ref9 = element.getAttribute("type")) != null ? ref9.toLowerCase() : void 0) === "hidden" || element.disabled || (element.readOnly && DomUtils.isSelectable(element)))); + break; + case "button": + case "select": + isClickable || (isClickable = !element.disabled); + break; + case "label": + isClickable || (isClickable = (element.control != null) && !element.control.disabled && (this.getVisibleClickable(element.control)).length === 0); + break; + case "body": + isClickable || (isClickable = element === document.body && !windowIsFocused() && window.innerWidth > 3 && window.innerHeight > 3 && ((ref10 = document.body) != null ? ref10.tagName.toLowerCase() : void 0) !== "frameset" ? reason = "Frame." : void 0); + //isClickable || (isClickable = element === document.body && windowIsFocused() && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "img": + isClickable || (isClickable = (ref11 = element.style.cursor) === "zoom-in" || ref11 === "zoom-out"); + break; + case "div": + case "ol": + case "ul": + //isClickable || (isClickable = element.clientHeight < element.scrollHeight && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "details": + isClickable = true; + reason = "Open."; + } + if (!isClickable && 0 <= ((ref12 = element.getAttribute("class")) != null ? ref12.toLowerCase().indexOf("button") : void 0)) { + possibleFalsePositive = isClickable = true; + } + tabIndexValue = element.getAttribute("tabindex"); + tabIndex = tabIndexValue === "" ? 0 : parseInt(tabIndexValue); + if (!(isClickable || isNaN(tabIndex) || tabIndex < 0)) { + isClickable = onlyHasTabIndex = true; + } + if (isClickable) { + clientRect = DomUtils.getVisibleClientRect(element, true); + if (clientRect !== null) { + visibleElements.push({ + element: element, + rect: clientRect, + secondClassCitizen: onlyHasTabIndex, + possibleFalsePositive: possibleFalsePositive, + reason: reason + }); + } + } + return visibleElements; + }, + getLocalHints: function(requireHref) { + var descendantsToCheck, element, elements, hint, i, k, l, left, len, len1, len2, len3, localHints, m, negativeRect, nonOverlappingElements, position, rects, ref, ref1, top, visibleElement, visibleElements; + if (!document.documentElement) { + return []; + } + elements = document.documentElement.getElementsByTagName("*"); + visibleElements = []; + for (i = 0, len = elements.length; i < len; i++) { + element = elements[i]; + if (!(requireHref && !element.href)) { + visibleElement = this.getVisibleClickable(element); + if (requireHref && element.href && visibleElement.length > 0) { + visibleElement[0]["href"] = element.href; + } + visibleElements.push.apply(visibleElements, visibleElement); + } + } + visibleElements = visibleElements.reverse(); + descendantsToCheck = [1, 2, 3]; + visibleElements = (function() { + var k, len1, results; + results = []; + for (position = k = 0, len1 = visibleElements.length; k < len1; position = ++k) { + element = visibleElements[position]; + if (element.possibleFalsePositive && (function() { + var _, candidateDescendant, index, l, len2; + index = Math.max(0, position - 6); + while (index < position) { + candidateDescendant = visibleElements[index].element; + for (l = 0, len2 = descendantsToCheck.length; l < len2; l++) { + _ = descendantsToCheck[l]; + candidateDescendant = candidateDescendant != null ? candidateDescendant.parentElement : void 0; + if (candidateDescendant === element.element) { + return true; + } + } + index += 1; + } + return false; + })()) { + continue; + } + results.push(element); + } + return results; + })(); + localHints = nonOverlappingElements = []; + while (visibleElement = visibleElements.pop()) { + rects = [visibleElement.rect]; + for (k = 0, len1 = visibleElements.length; k < len1; k++) { + negativeRect = visibleElements[k].rect; + rects = (ref = []).concat.apply(ref, rects.map(function(rect) { + return Rect.subtract(rect, negativeRect); + })); + } + if (rects.length > 0) { + try { + var testextend = extend(visibleElement, { rect: rects[0] }); + nonOverlappingElements.push(testextend); + } catch(error) { + nonOverlappingElements.push(visibleElement); + } + /*nonOverlappingElements.push(extend(visibleElement, { + rect: rects[0] + }));*/ + } else { + if (!visibleElement.secondClassCitizen) { + nonOverlappingElements.push(visibleElement); + } + } + } + ref1 = DomUtils.getViewportTopLeft(), top = ref1.top, left = ref1.left; + for (l = 0, len2 = nonOverlappingElements.length; l < len2; l++) { + hint = nonOverlappingElements[l]; + hint.rect.top += top; + hint.rect.left += left; + } + /*if (Settings.get("filterLinkHints")) { + for (m = 0, len3 = localHints.length; m < len3; m++) { + hint = localHints[m]; + extend(hint, this.generateLinkText(hint)); + } + }*/ + return localHints; + }, + generateLinkText: function(hint) { + var element, linkText, nodeName, ref, showLinkText; + element = hint.element; + linkText = ""; + showLinkText = false; + nodeName = element.nodeName.toLowerCase(); + if (nodeName === "input") { + if ((element.labels != null) && element.labels.length > 0) { + linkText = element.labels[0].textContent.trim(); + if (linkText[linkText.length - 1] === ":") { + linkText = linkText.slice(0, linkText.length - 1); + } + showLinkText = true; + } else if (((ref = element.getAttribute("type")) != null ? ref.toLowerCase() : void 0) === "file") { + linkText = "Choose File"; + } else if (element.type !== "password") { + linkText = element.value; + if (!linkText && 'placeholder' in element) { + linkText = element.placeholder; + } + } + } else if (nodeName === "a" && !element.textContent.trim() && element.firstElementChild && element.firstElementChild.nodeName.toLowerCase() === "img") { + linkText = element.firstElementChild.alt || element.firstElementChild.title; + if (linkText) { + showLinkText = true; + } + } else if (hint.reason != null) { + linkText = hint.reason; + showLinkText = true; + } else if (0 < element.textContent.length) { + linkText = element.textContent.slice(0, 256); + } else if (element.hasAttribute("title")) { + linkText = element.getAttribute("title"); + } else { + linkText = element.innerHTML.slice(0, 256); + } + return { + linkText: linkText.trim(), + showLinkText: showLinkText + }; + } + }; + +var VimiumNormal = { + followLink : function(linkElement) { + if (linkElement.nodeName.toLowerCase() === "link") { + return window.location.href = linkElement.href; + } else { + linkElement.scrollIntoView(); + return DomUtils.simulateClick(linkElement); + } + }, + findAndFollowLink : function(linkStrings) { + var boundingClientRect, candidateLink, candidateLinks, computedStyle, exactWordRegex, i, j, k, l, len, len1, len2, len3, link, linkMatches, linkString, links, linksXPath, m, n, ref, ref1; + linksXPath = DomUtils.makeXPath(["a", "*[@onclick or @role='link' or contains(@class, 'button')]"]); + links = DomUtils.evaluateXPath(linksXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + candidateLinks = []; + for (i = j = ref = links.snapshotLength - 1; j >= 0; i = j += -1) { + link = links.snapshotItem(i); + boundingClientRect = link.getBoundingClientRect(); + if (boundingClientRect.width === 0 || boundingClientRect.height === 0) { + continue; + } + computedStyle = window.getComputedStyle(link, null); + if (computedStyle.getPropertyValue("visibility") !== "visible" || computedStyle.getPropertyValue("display") === "none") { + continue; + } + linkMatches = false; + for (k = 0, len = linkStrings.length; k < len; k++) { + linkString = linkStrings[k]; + if (link.innerText.toLowerCase().indexOf(linkString) !== -1 || 0 <= ((ref1 = link.value) != null ? typeof ref1.indexOf === "function" ? ref1.indexOf(linkString) : void 0 : void 0)) { + linkMatches = true; + break; + } + } + if (!linkMatches) { + continue; + } + candidateLinks.push(link); + } + if (candidateLinks.length === 0) { + return; + } + for (l = 0, len1 = candidateLinks.length; l < len1; l++) { + link = candidateLinks[l]; + link.wordCount = link.innerText.trim().split(/\s+/).length; + } + candidateLinks.forEach(function(a, i) { + return a.originalIndex = i; + }); + candidateLinks = candidateLinks.sort(function(a, b) { + if (a.wordCount === b.wordCount) { + return a.originalIndex - b.originalIndex; + } else { + return a.wordCount - b.wordCount; + } + }).filter(function(a) { + return a.wordCount <= candidateLinks[0].wordCount + 1; + }); + for (m = 0, len2 = linkStrings.length; m < len2; m++) { + linkString = linkStrings[m]; + exactWordRegex = /\b/.test(linkString[0]) || /\b/.test(linkString[linkString.length - 1]) ? new RegExp("\\b" + linkString + "\\b", "i") : new RegExp(linkString, "i"); + for (n = 0, len3 = candidateLinks.length; n < len3; n++) { + candidateLink = candidateLinks[n]; + if (exactWordRegex.test(candidateLink.innerText) || (candidateLink.value && exactWordRegex.test(candidateLink.value))) { + this.followLink(candidateLink); + return true; + } + } + } + return false; + }, + findAndFollowRel : function(value) { + var element, elements, j, k, len, len1, relTags, tag; + relTags = ["link", "a", "area"]; + for (j = 0, len = relTags.length; j < len; j++) { + tag = relTags[j]; + elements = document.getElementsByTagName(tag); + for (k = 0, len1 = elements.length; k < len1; k++) { + element = elements[k]; + if (element.hasAttribute("rel") && element.rel.toLowerCase() === value) { + this.followLink(element); + return true; + } + } + } + }, + textInputXPath : function() { + var inputElements, textInputTypes; + textInputTypes = ["text", "search", "email", "url", "number", "password", "date", "tel"]; + inputElements = [ + "input[" + "(" + textInputTypes.map(function(type) { + return '@type="' + type + '"'; + }).join(" or ") + "or not(@type))" + " and not(@disabled or @readonly)]", "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" + ]; + return typeof DomUtils !== "undefined" && DomUtils !== null ? DomUtils.makeXPath(inputElements) : void 0; + }, + focusInput: function(count) { + var element, elements, hint, hints, i, recentlyFocusedElement, resultSet, selectedInputIndex, tuple, visibleInputs; + resultSet = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + visibleInputs = (function() { + var j, ref, results; + results = []; + for (i = j = 0, ref = resultSet.snapshotLength; j < ref; i = j += 1) { + element = resultSet.snapshotItem(i); + if (!DomUtils.getVisibleClientRect(element, true)) { + continue; + } + results.push({ + element: element, + index: i, + rect: Rect.copy(element.getBoundingClientRect()) + }); + } + return results; + })(); + visibleInputs.sort(function(arg, arg1) { + var element1, element2, i1, i2, tabDifference; + element1 = arg.element, i1 = arg.index; + element2 = arg1.element, i2 = arg1.index; + if (element1.tabIndex > 0) { + if (element2.tabIndex > 0) { + tabDifference = element1.tabIndex - element2.tabIndex; + if (tabDifference !== 0) { + return tabDifference; + } else { + return i1 - i2; + } + } else { + return -1; + } + } else if (element2.tabIndex > 0) { + return 1; + } else { + return i1 - i2; + } + }); + if (visibleInputs.length === 0) { + HUD.showForDuration("There are no inputs to focus.", 1000); + return; + } + recentlyFocusedElement = lastFocusedInput(); + selectedInputIndex = count === 1 ? (elements = visibleInputs.map(function(visibleInput) { + return visibleInput.element; + }), Math.max(0, elements.indexOf(recentlyFocusedElement))) : Math.min(count, visibleInputs.length) - 1; + hints = (function() { + var j, len, results; + results = []; + for (j = 0, len = visibleInputs.length; j < len; j++) { + tuple = visibleInputs[j]; + hint = DomUtils.createElement("div"); + hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"; + hint.style.left = (tuple.rect.left - 1) + window.scrollX + "px"; + hint.style.top = (tuple.rect.top - 1) + window.scrollY + "px"; + hint.style.width = tuple.rect.width + "px"; + hint.style.height = tuple.rect.height + "px"; + results.push(hint); + } + return results; + })(); + return new FocusSelector(hints, visibleInputs, selectedInputIndex); + } + }; + + +export function MiscVimium() { + if (window.forTrusted == null) { + window.forTrusted = function(handler) { + return function(event) { + if (event != null ? event.isTrusted : void 0) { + return handler.apply(this, arguments); + } else { + return true; + } + }; + }; + } + window.windowIsFocused = function() { + var windowHasFocus; + windowHasFocus = null; + DomUtils.documentReady(function() { + return windowHasFocus = document.hasFocus(); + }); + window.addEventListener("focus", forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = true; + } + return true; + })); + window.addEventListener("blur", forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = false; + } + return true; + })); + return function() { + return windowHasFocus; + }; + }; +}; + +export { Rect } +export { DomUtils } +export { LocalHints } +export { VimiumNormal } + + From ced13791d3ccbda2cf3b5d26b6115a8e9700f76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:52:00 -0400 Subject: [PATCH 02/77] Created overlayVimMode function for displaying vim navigation state --- interfacer/src/browsh/ui.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index d1781825..6c70b777 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -108,6 +108,41 @@ func urlBarFocus(on bool) { } } +func overlayVimMode() { + _, height := screen.Size() + switch vimMode { + case InsertMode: + writeString(0, height-1, "ins", tcell.StyleDefault) + case LinkMode: + writeString(0, height-1, "lnk", tcell.StyleDefault) + case LinkModeNewTab: + writeString(0, height-1, "LNK", tcell.StyleDefault) + case LinkModeCopy: + writeString(0, height-1, "cp", tcell.StyleDefault) + case VisualMode: + writeString(0, height-1, "vis", tcell.StyleDefault) + case CaretMode: + writeString(0, height-1, "car", tcell.StyleDefault) + writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) + case FindMode: + writeString(0, height-1, "/" + findText, tcell.StyleDefault) + case MakeMarkMode: + writeString(0, height-1, "mark", tcell.StyleDefault) + case GotoMarkMode: + writeString(0, height-1, "goto", tcell.StyleDefault) + } + + switch vimMode { + case LinkMode, LinkModeNewTab, LinkModeCopy: + if !linkModeWithHints { + findAndHighlightTextOnScreen(linkText) } + + if linkHintWriteStringCalls != nil { + (*linkHintWriteStringCalls)() + } + } +} + func overlayPageStatusMessage() { _, height := screen.Size() writeString(0, height-1, CurrentTab.StatusMessage, tcell.StyleDefault) From 6bd4c9699b3d7ecb3a9d8fc2763ff19e052210e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:57:43 -0400 Subject: [PATCH 03/77] gofmt code --- interfacer/src/browsh/ui.go | 61 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 6c70b777..76de8471 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -109,38 +109,39 @@ func urlBarFocus(on bool) { } func overlayVimMode() { - _, height := screen.Size() - switch vimMode { - case InsertMode: - writeString(0, height-1, "ins", tcell.StyleDefault) - case LinkMode: - writeString(0, height-1, "lnk", tcell.StyleDefault) - case LinkModeNewTab: - writeString(0, height-1, "LNK", tcell.StyleDefault) - case LinkModeCopy: - writeString(0, height-1, "cp", tcell.StyleDefault) - case VisualMode: - writeString(0, height-1, "vis", tcell.StyleDefault) - case CaretMode: - writeString(0, height-1, "car", tcell.StyleDefault) - writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) - case FindMode: - writeString(0, height-1, "/" + findText, tcell.StyleDefault) - case MakeMarkMode: - writeString(0, height-1, "mark", tcell.StyleDefault) - case GotoMarkMode: - writeString(0, height-1, "goto", tcell.StyleDefault) - } + _, height := screen.Size() + switch vimMode { + case InsertMode: + writeString(0, height-1, "ins", tcell.StyleDefault) + case LinkMode: + writeString(0, height-1, "lnk", tcell.StyleDefault) + case LinkModeNewTab: + writeString(0, height-1, "LNK", tcell.StyleDefault) + case LinkModeCopy: + writeString(0, height-1, "cp", tcell.StyleDefault) + case VisualMode: + writeString(0, height-1, "vis", tcell.StyleDefault) + case CaretMode: + writeString(0, height-1, "car", tcell.StyleDefault) + writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) + case FindMode: + writeString(0, height-1, "/"+findText, tcell.StyleDefault) + case MakeMarkMode: + writeString(0, height-1, "mark", tcell.StyleDefault) + case GotoMarkMode: + writeString(0, height-1, "goto", tcell.StyleDefault) + } - switch vimMode { - case LinkMode, LinkModeNewTab, LinkModeCopy: - if !linkModeWithHints { - findAndHighlightTextOnScreen(linkText) } + switch vimMode { + case LinkMode, LinkModeNewTab, LinkModeCopy: + if !linkModeWithHints { + findAndHighlightTextOnScreen(linkText) + } - if linkHintWriteStringCalls != nil { - (*linkHintWriteStringCalls)() - } - } + if linkHintWriteStringCalls != nil { + (*linkHintWriteStringCalls)() + } + } } func overlayPageStatusMessage() { From a78d98bdd69d44fe8164d510318b4ba68b5a0755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:59:09 -0400 Subject: [PATCH 04/77] added command for links hints --- interfacer/src/browsh/comms.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index 494af081..b2eb67d1 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -81,6 +81,8 @@ func handleWebextensionCommand(message []byte) { } case "/screenshot": saveScreenshot(parts[1]) + case "/link_hints": + parseJSONLinkHints(strings.Join(parts[1:], ",")) default: Log("WEBEXT: " + string(message)) } From 8b35a6889aa86cf77127e965c28a66c9dae755e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 15:00:23 -0400 Subject: [PATCH 05/77] Set default DISPLAY environment variable for xclipboard functionality --- interfacer/src/browsh/browsh.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index bcb7edc6..4647e42b 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -180,6 +180,12 @@ func ttyEntry() { // from tcell. os.Setenv("TERM", "xterm-truecolor") } + // This is for getting the clipboard (github.com/atotto/clipboard) to work + // with the applications xsel and xclip on systems with an X display server. + if os.Getenv("DISPLAY") == "" { + os.Setenv("DISPLAY", ":0") + } + realScreen, err := tcell.NewScreen() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) From 3c8afeda5e3eee1ec6643836bd671084a8182572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 15:10:11 -0400 Subject: [PATCH 06/77] ignore manifest.json backup file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 539a2ec0..51c4caf8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ interfacer/browsh webextension.go webext/node_modules webext/dist/* +webext/manifest.json~ dist *.xpi From a04bdac73a8fd4e16fa19803a54e6b24b3bb1387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Thu, 18 Oct 2018 05:30:56 -0400 Subject: [PATCH 07/77] ignore debug log in interfacer/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 51c4caf8..59a770c8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ interfacer/vendor interfacer/dist interfacer/interfacer interfacer/browsh +interfacer/debug webextension.go webext/node_modules webext/dist/* From 0d2dfee777c54782f2573b45eb910eb9e19920a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Wed, 24 Oct 2018 13:48:54 -0400 Subject: [PATCH 08/77] Fixed typo in comment --- interfacer/src/browsh/input_box.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index b69010dc..405096e4 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -12,7 +12,7 @@ var activeInputBox *inputBox // A box into which you can enter text. Generally will be forwarded to a standard // HTML input box in the real browser. // -// Note that tcell alreay has some ready-made code in its 'views' concept for +// Note that tcell already has some ready-made code in its 'views' concept for // dealing with input areas. However, at the time of writing it wasn't well documented, // so it was unclear how easy it would be to integrate the requirements of Browsh's // input boxes - namely overlaying them onto the existing graphics and having them From 9329c9f830787adbd76249bd046b2de189733626 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 06:39:36 -0400 Subject: [PATCH 09/77] Added sessions permission --- webext/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webext/manifest.json b/webext/manifest.json index bd6dc0fc..0d26d3a1 100644 --- a/webext/manifest.json +++ b/webext/manifest.json @@ -32,6 +32,7 @@ "", "webRequest", "webRequestBlocking", - "tabs" + "tabs", + "sessions" ] } From c40c7245647a7f87d2f7075b0bb9c02a405b22ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 09:11:24 -0400 Subject: [PATCH 10/77] Added initial configuration for vim like keybindings. The keybindings are hardcoded for now, but this is going to change. --- interfacer/src/browsh/config.go | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go index a60b0722..7fa5bb33 100644 --- a/interfacer/src/browsh/config.go +++ b/interfacer/src/browsh/config.go @@ -72,6 +72,51 @@ func getFirefoxProfilePath() string { func setDefaults() { // Temporary experimental configurable keybindings viper.SetDefault("tty.keys.next-tab", []string{"\u001c", "28", "2"}) + + // Vim commands + vimCommandsBindings["gg"] = "scrollToTop" + vimCommandsBindings["G"] = "scrollToBottom" + vimCommandsBindings["j"] = "scrollDown" + vimCommandsBindings["k"] = "scrollUp" + vimCommandsBindings["h"] = "scrollLeft" + vimCommandsBindings["l"] = "scrollRight" + vimCommandsBindings["d"] = "scrollHalfPageDown" + vimCommandsBindings["u"] = "scrollHalfPageUp" + vimCommandsBindings["e"] = "editURL" + vimCommandsBindings["ge"] = "editURL" + vimCommandsBindings["H"] = "historyBack" + vimCommandsBindings["L"] = "historyForward" + vimCommandsBindings["J"] = "prevTab" + vimCommandsBindings["K"] = "nextTab" + vimCommandsBindings["r"] = "reload" + vimCommandsBindings["xx"] = "removeTab" + vimCommandsBindings["X"] = "restoreTab" + vimCommandsBindings["t"] = "newTab" + vimCommandsBindings["/"] = "findMode" + vimCommandsBindings["n"] = "findNext" + vimCommandsBindings["N"] = "findPrevious" + vimCommandsBindings["g0"] = "firstTab" + vimCommandsBindings["g$"] = "lastTab" + vimCommandsBindings["gu"] = "urlUp" + vimCommandsBindings["gU"] = "urlRoot" + vimCommandsBindings["<<"] = "moveTabLeft" + vimCommandsBindings[">>"] = "moveTabRight" + vimCommandsBindings["^"] = "previouslyVisitedTab" + vimCommandsBindings["m"] = "makeMark" + vimCommandsBindings["'"] = "gotoMark" + vimCommandsBindings["i"] = "insertMode" + vimCommandsBindings["yy"] = "copyURL" + vimCommandsBindings["p"] = "openClipboardURL" + vimCommandsBindings["P"] = "openClipboardURLInNewTab" + vimCommandsBindings["gi"] = "focusFirstTextInput" + vimCommandsBindings["f"] = "openLinkInCurrentTab" + vimCommandsBindings["F"] = "openLinkInNewTab" + vimCommandsBindings["yf"] = "copyLinkURL" + vimCommandsBindings["[["] = "followLinkLabeledPrevious" + vimCommandsBindings["]]"] = "followLinkLabeledNext" + vimCommandsBindings["yt"] = "duplicateTab" + vimCommandsBindings["v"] = "visualMode" + vimCommandsBindings["?"] = "viewHelp" } func loadConfig() { From f730983189d31e194dd15e2376d37695c8785e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 09:14:40 -0400 Subject: [PATCH 11/77] Added duplicate_tab, restore_tab commands. --- webext/src/background/tty_commands_mixin.js | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/webext/src/background/tty_commands_mixin.js b/webext/src/background/tty_commands_mixin.js index 6fef3f75..3886f163 100644 --- a/webext/src/background/tty_commands_mixin.js +++ b/webext/src/background/tty_commands_mixin.js @@ -29,9 +29,15 @@ export default MixinBase => case "/switch_to_tab": this.switchToTab(parts.slice(1).join(",")); break; + case "/duplicate_tab": + this.duplicateTab(parts.slice(1).join(",")); + break; case "/remove_tab": this.removeTab(parts.slice(1).join(",")); break; + case "/restore_tab": + this.restoreTab(); + break; case "/raw_text_request": this._rawTextRequest(parts[1], parts[2], parts.slice(3).join(",")); break; @@ -173,6 +179,24 @@ export default MixinBase => this.tabs[id] = null; } + duplicateTab(id) { + browser.tabs.duplicate(parseInt(id)); + } + + restoreTab() { + var sessionsInfo = browser.sessions.getRecentlyClosed({maxResults: 1 }); + sessionsInfo.then(this._restoreTab); + } + + _restoreTab(sessionsInfo) { + var mySessionInfo = sessionsInfo[0]; + if (mySessionInfo.tab) { + browser.sessions.restore(mySessionInfo.tab.sessionId); + } else { + browser.sessions.restore(mySessionInfo.window.sessionId); + } + } + // We use the `browser` object here rather than going into the actual content script // because the content script may have crashed, even never loaded. screenshotActiveTab() { From b78d896b9b83880df0028d3d81b27b4569bbe2bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 09:20:52 -0400 Subject: [PATCH 12/77] Refactored code using switchToTab and added new features. Added moveTabLeft and moveTabRight functions, which take a tab ID and try to move the tab as far right or left in the tabOrder as possible. Added previouslyVisitedTab function that switches to the previously selected tab. --- interfacer/src/browsh/tab.go | 75 ++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index 1b330dd6..04953451 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -18,6 +18,9 @@ var tabsOrder []int // the tab being deleted, so we need to keep track of all deleted IDs var tabsDeleted []int +// ID of the tab that was active before the current one +var previouslyVisitedTabID int + // A single tab synced from the browser type tab struct { ID int `json:"id"` @@ -54,6 +57,10 @@ func newTab(id int) { } } +func restoreTab() { + sendMessageToWebExtension("/restore_tab") +} + func removeTab(id int) { if len(Tabs) == 1 { quitBrowsh() @@ -77,6 +84,40 @@ func removeTabIDfromTabsOrder(id int) { } } +func moveTabLeft(id int) { + // If the tab ID is already completely to the left in the tab order + // there's nothing to do + if tabsOrder[0] == id { + return + } + + for i, tabId := range tabsOrder { + if tabId == id { + tabsOrder[i-1], tabsOrder[i] = tabsOrder[i], tabsOrder[i-1] + break + } + } +} + +func moveTabRight(id int) { + // If the tab ID is already completely to the right in the tab order + // there's nothing to do + if tabsOrder[len(tabsOrder)-1] == id { + return + } + + for i, tabId := range tabsOrder { + if tabId == id { + tabsOrder[i+1], tabsOrder[i] = tabsOrder[i], tabsOrder[i+1] + break + } + } +} + +func duplicateTab(id int) { + sendMessageToWebExtension(fmt.Sprintf("/duplicate_tab,%d", id)) +} + // Creating a new tab in the browser without a URI means it won't register with the // web extension, which means that, come the moment when we actually have a URI for the new // tab then we can't talk to it to tell it navigate. So we need to only create a real new @@ -109,15 +150,41 @@ func nextTab() { } else { i++ } - sendMessageToWebExtension(fmt.Sprintf("/switch_to_tab,%d", tabsOrder[i])) - CurrentTab = Tabs[tabsOrder[i]] - renderUI() - renderCurrentTabWindow() + switchToTab(tabsOrder[i]) break } } } +func prevTab() { + for i := 0; i < len(tabsOrder); i++ { + if tabsOrder[i] == CurrentTab.ID { + if i-1 < 0 { + i = len(tabsOrder) - 1 + } else { + i-- + } + switchToTab(tabsOrder[i]) + break + } + } +} + +func previouslyVisitedTab() { + if previouslyVisitedTabID == 0 { + return + } + switchToTab(previouslyVisitedTabID) +} + +func switchToTab(num int) { + sendMessageToWebExtension(fmt.Sprintf("/switch_to_tab,%d", num)) + previouslyVisitedTabID = CurrentTab.ID + CurrentTab = Tabs[num] + renderUI() + renderCurrentTabWindow() +} + func isTabPreviouslyDeleted(id int) bool { for i := 0; i < len(tabsDeleted); i++ { if tabsDeleted[i] == id { From 052aecdc9b4089b7c836586e10ebf357fa759484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:05:09 -0400 Subject: [PATCH 13/77] Added features needed for vim like navigation to the webextension. --- webext/src/background/tab_commands_mixin.js | 3 + webext/src/dom/commands_mixin.js | 189 +++++++++++++++++++- 2 files changed, 183 insertions(+), 9 deletions(-) diff --git a/webext/src/background/tab_commands_mixin.js b/webext/src/background/tab_commands_mixin.js index d26baeb9..99f66c83 100644 --- a/webext/src/background/tab_commands_mixin.js +++ b/webext/src/background/tab_commands_mixin.js @@ -17,6 +17,9 @@ export default MixinBase => case "/frame_pixels": this.sendToTerminal(`/frame_pixels,${message.slice(14)}`); break; + case "/link_hints": + this.sendToTerminal(`/link_hints,${message.slice(12)}`); + break; case "/tab_info": incoming = JSON.parse(utils.rebuildArgsToSingleArg(parts)); this._updateTabInfo(incoming); diff --git a/webext/src/dom/commands_mixin.js b/webext/src/dom/commands_mixin.js index 66926323..dd608c94 100644 --- a/webext/src/dom/commands_mixin.js +++ b/webext/src/dom/commands_mixin.js @@ -1,4 +1,10 @@ import utils from "utils"; +import { Rect } from "vimium"; +import { DomUtils } from "vimium"; +import { LocalHints } from "vimium"; +import { VimiumNormal } from "vimium"; +import { MiscVimium } from "vimium"; +MiscVimium(); export default MixinBase => class extends MixinBase { @@ -35,19 +41,148 @@ export default MixinBase => break; case "/url": url = utils.rebuildArgsToSingleArg(parts); - document.location.href = url; + window.location.href = url; + break; + case "/url_up": + this.urlUp(); + break; + case "/url_root": + window.location.href = window.location.origin; break; case "/history_back": history.go(-1); break; + case "/history_forward": + history.go(1); + break; + case "/reload": + window.location.reload(); + break; case "/window_stop": window.stop(); break; + case "/find_next": + this.findNext(parts[1]); + break; + case "/find_previous": + window.find(parts[1], false, true, false, false, true, true); + break; + case "/get_link_hints": + this.getLinkHints(false); + break; + case "/get_clickable_hints": + this.getLinkHints(true); + break; + case "/focus_first_text_input": + this.focusFirstTextInput(); + break; + case "/follow_link_labeled_next": + this._followLinkLabeledNext(); + break; + case "/follow_link_labeled_previous": + this._followLinkLabeledPrevious(); + break; default: this.log("Unknown command sent to tab", message); } } + focusFirstTextInput() { + VimiumNormal.focusInput(1); + } + + //adapted vimium code + followLinkLabeledNext() { + var nextPatterns = "next,more,newer,>,›,→,»,≫,>>,weiter" || ""; + var nextStrings = nextPatterns.split(",").filter(function(s) { + return s.trim().length; + }); + return ( + VimiumNormal.findAndFollowRel("next") || + VimiumNormal.findAndFollowLink(nextStrings) + ); + } + + _followLinkLabeledNext() { + this.followLinkLabeledNext(); + } + + //adapted vimium code + followLinkLabeledPrevious() { + var previousPatterns = + "prev,previous,back,older,<,‹,←,«,≪,<<,zurück" || ""; + var previousStrings = previousPatterns.split(",").filter(function(s) { + return s.trim().length; + }); + return ( + VimiumNormal.findAndFollowRel("prev") || + VimiumNormal.findAndFollowLink(previousStrings) + ); + } + + _followLinkLabeledPrevious() { + this.followLinkLabeledPrevious(); + } + + urlUp() { + // this is taken from vimium's code + var url = window.location.href; + if (url[url.length - 1] === "/") { + url = url.substring(0, url.length - 1); + } + var urlsplit = url.split("/"); + // make sure we haven't hit the base domain yet + if (urlsplit.length > 3) { + urlsplit = urlsplit.slice(0, Math.max(3, urlsplit.length - 1)); + window.location.href = urlsplit.join("/"); + } + } + + getLinkHints(clickable) { + var hints = LocalHints.getLocalHints(!clickable); + var rect, bottom, top, left, right, width, height, results, result, href; + results = []; + for (let idx in hints) { + if (!hints[idx].hasOwnProperty("rect")) { + continue; + } + href = hints[idx]["href"]; + rect = hints[idx]["rect"]; + bottom = Math.round( + ((rect["bottom"] - window.scrollY) * + this.dimensions.scale_factor.height) / + 2 + ); + top = Math.round( + ((rect["top"] - window.scrollY) * + this.dimensions.scale_factor.height) / + 2 + ); + left = Math.round(rect["left"] * this.dimensions.scale_factor.width); + right = Math.round(rect["right"] * this.dimensions.scale_factor.width); + result = Rect.create(left, top, right, bottom); + result.href = href; + results.push(result); + } + this.sendMessage(`/link_hints,${JSON.stringify(results)}`); + } + + findNext(text) { + window.find(text, false, false, false, false, true, true); + //var s = window.getSelection(); + //var oRange = s.getRangeAt(0); //get the text range + //var oRect = oRange.getBoundingClientRect(); + //window.scrollTo(400, 20000); + this.dimensions.y_scroll = Math.round( + window.scrollY * this.dimensions.scale_factor.height + ); + this.dimensions.x_scroll = Math.round( + window.scrollX * this.dimensions.scale_factor.width + ); + this.dimensions.update(); + this._mightSendBigFrames(); + } + _launch() { const mode = this.config.http_server_mode_type; if (mode === "raw_text_plain" || mode === "raw_text_html") { @@ -108,9 +243,25 @@ export default MixinBase => _handleMouse(input) { switch (input.button) { case 1: - this._mouseAction("mousemove", input.mouse_x, input.mouse_y); + var y_hack = false; + if (input.hasOwnProperty("y_hack")) { + y_hack = true; + } + this._mouseAction( + "mousemove", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); if (!this._mousedown) { - this._mouseAction("mousedown", input.mouse_x, input.mouse_y); + this._mouseAction( + "mousedown", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); setTimeout(() => { this.sendSmallTextFrame(); }, 500); @@ -118,10 +269,26 @@ export default MixinBase => this._mousedown = true; break; case 0: - this._mouseAction("mousemove", input.mouse_x, input.mouse_y); + var y_hack = false; + if (input.hasOwnProperty("y_hack")) { + y_hack = true; + } + this._mouseAction( + "mousemove", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); if (this._mousedown) { - this._mouseAction("click", input.mouse_x, input.mouse_y); - this._mouseAction("mouseup", input.mouse_x, input.mouse_y); + this._mouseAction("click", input.mouse_x, input.mouse_y, 0, y_hack); + this._mouseAction( + "mouseup", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); } this._mousedown = false; break; @@ -169,8 +336,12 @@ export default MixinBase => } } - _mouseAction(type, x, y) { - const [dom_x, dom_y] = this._getDOMCoordsFromMouseCoords(x, y); + _mouseAction(type, x, y, button, y_hack = false) { + let [dom_x, dom_y] = this._getDOMCoordsFromMouseCoords(x, y); + if (y_hack) { + const [dom_x2, dom_y2] = this._getDOMCoordsFromMouseCoords(x, y + 1); + dom_y = (dom_y + dom_y2) / 2; + } const element = document.elementFromPoint( dom_x - window.scrollX, dom_y - window.scrollY @@ -191,7 +362,7 @@ export default MixinBase => false, false, false, - 0, + button, null ); element.dispatchEvent(clickEvent); From 4099f51f838166d07ce7ce310c53bfad9ab9db39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:18:39 -0400 Subject: [PATCH 14/77] Added vim like navigation. This is still in an early stage. --- interfacer/src/browsh/tty.go | 677 +++++++++++++++++++++++++++++++++-- 1 file changed, 650 insertions(+), 27 deletions(-) diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index 6b33dc46..7546e303 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -4,20 +4,110 @@ import ( "encoding/json" "fmt" "os" + "reflect" "strconv" + "strings" + "time" + "unicode" + "github.com/atotto/clipboard" "github.com/gdamore/tcell" "github.com/go-errors/errors" "github.com/spf13/viper" ) +type VimMode int + +const ( + NormalMode VimMode = iota + 1 + InsertMode + FindMode + LinkMode + LinkModeNewTab + LinkModeCopy + WaitMode + VisualMode + CaretMode + MakeMarkMode + GotoMarkMode +) + +type mark struct { + tabID int + URI string + xScroll int + yScroll int +} + +type Coordinate struct { + X, Y int +} + +type HintRect struct { + Bottom int `json:"bottom"` + Top int `json:"top"` + Left int `json:"left"` + Right int `json:"right"` + Width int `json:"width"` + Height int `json:"height"` + Href string `json:"href"` +} + var ( screen tcell.Screen uiHeight = 2 // IsMonochromeMode decides whether to render the TTY in full colour or monochrome IsMonochromeMode = false + // vimMode + vimMode = NormalMode + vimCommandsBindings = make(map[string]string) + keyEvents = make([]*tcell.EventKey, 11) + //runeTime = make(map[rune]time.Time) + //lastRune rune + waitModeStartTime time.Time + findText string + // marks + globalMarkMap = make(map[rune]*mark) + localMarkMap = make(map[int]map[rune]*mark) + // position coordinate for caret mode + caretPos Coordinate + // for link modes + linkText string + linkHintRects []HintRect + linkHintKeys = "asdfwerxcv" + linkHints []string + linkHintsToRects = make(map[string]*HintRect) + linkModeWithHints = true + linkHintWriteStringCalls *func() ) +func init() { + setupLinkHints() +} + +func setupLinkHints() { + lowerAlpha := "abcdefghijklmnopqrstuvwxyz" + missingAlpha := lowerAlpha + + // Use linkHintKeys first to generate link hints + for i := 0; i < len(linkHintKeys); i++ { + for j := 0; j < len(linkHintKeys); j++ { + linkHints = append(linkHints, string(linkHintKeys[i])+string(linkHintKeys[j])) + } + missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) + } + + // missingAlpha contains all keys that aren't in linkHintKeys + // we use this to generate the last link hint key combinations, + // so this will only be used when we run out of linkHintKeys based + // link hint key combinations. + for i := 0; i < len(missingAlpha); i++ { + for j := 0; j < len(lowerAlpha); j++ { + linkHints = append(linkHints, string(missingAlpha[i])+string(lowerAlpha[j])) + } + } +} + func setupTcell() { var err error if err = screen.Init(); err != nil { @@ -87,6 +177,7 @@ func handleUserKeyPress(ev *tcell.EventKey) { if activeInputBox != nil { handleInputBoxInput(ev) } else { + handleVimControl(ev) handleScrolling(ev) // TODO: shouldn't you be able to still use mouse scrolling? } } @@ -138,34 +229,481 @@ func isMultiLineEnter(ev *tcell.EventKey) bool { return activeInputBox.isMultiLine() && ev.Key() == 13 && ev.Modifiers() != 4 } -func handleScrolling(ev *tcell.EventKey) { +// Moves the caret in CaretMode. +// testFunc is a function that tests for the reaching of the boundaries of the given axis. +// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. +// The step size and direction is given by the value of step. +func moveVimCaret(testFunc func() bool, valRef *int, step int) { + var prevCell, nextCell, nextNextCell cell + var r rune + hasNextNextCell := false + + for testFunc() { + prevCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef += step + nextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + + if testFunc() { + *valRef += step + nextNextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef -= step + hasNextNextCell = true + } else { + hasNextNextCell = false + } + + r = nextCell.character[0] + // Check if the next cell is different in any way + if !reflect.DeepEqual(prevCell, nextCell) { + if hasNextNextCell { + // This condition should apply to the spaces between words and the like + // Checking with unicode.isSpace() didn't give correct results for some reason + // TODO: find out what that reason is and improve this + if !unicode.IsLetter(r) && unicode.IsLetter(nextNextCell.character[0]) { + continue + } + // If the upcoming cell is deeply equal we can continue to go forward + if reflect.DeepEqual(nextCell, nextNextCell) { + continue + } + } + // This cell is different and other conditions for continuing don't apply + // therefore we stop going forward. + break + } + } +} + +// TODO: This fails if the tab with mark.tabID doesn't exist anymore it should recreate said tab, then go to the mark's URL and position +func gotoMark(mark *mark) { + if CurrentTab.ID != mark.tabID { + ensureTabExists(mark.tabID) + switchToTab(mark.tabID) + } + if CurrentTab.URI != mark.URI { + sendMessageToWebExtension("/tab_command,/url," + mark.URI) + //sleep? + } + doScrollAbsolute(mark.xScroll, mark.yScroll) +} + +// Make a mark at the current position in the current tab +func makeMark() *mark { + return &mark{CurrentTab.ID, CurrentTab.URI, CurrentTab.frame.xScroll, CurrentTab.frame.yScroll} +} + +func generateLeftClickYHack(x, y int, yHack bool) { + newMouseEvent := tcell.NewEventMouse(x, y+uiHeight, tcell.Button1, 0) + handleMouseEventYHack(newMouseEvent, yHack) + time.Sleep(time.Millisecond * 100) + newMouseEvent = tcell.NewEventMouse(x, y+uiHeight, 0, 0) + handleMouseEventYHack(newMouseEvent, yHack) +} + +func generateLeftClick(x, y int) { + generateLeftClickYHack(x, y, false) +} + +// TODO: This isn't working for opening new tabs. +func generateMiddleClick(x, y int) { + newMouseEvent := tcell.NewEventMouse(x, y+uiHeight, tcell.Button2, 0) + handleMouseEvent(newMouseEvent) + time.Sleep(time.Millisecond * 100) + newMouseEvent = tcell.NewEventMouse(x, y+uiHeight, 0, 0) + handleMouseEvent(newMouseEvent) +} + +func goIntoWaitMode() { + vimMode = WaitMode + waitModeStartTime = time.Now() +} + +func updateLinkHintDisplay() { + linkHintsToRects = make(map[string]*HintRect) + lh := len(linkHintRects) + var ht string + // List of closures + var fc []*func() + + for i, r := range linkHintRects { + // When the number of link hints is small enough + // using just one key for individual link hints suffices. + // Otherwise use the prepared link hint key combinations. + if lh <= len(linkHintKeys) { + ht = string(linkHintKeys[i]) + } else { + ht = linkHints[i] + } + // Add the key combination ht to the linkHintsToRects map. + // When the user presses it, we can easily lookup the + // link hint properties associated with it. + linkHintsToRects[ht] = &linkHintRects[i] + + // When the first key got hit, + // shorten the link hints accordingly + offsetLeft := 0 + if strings.HasPrefix(ht, linkText) { + ht = ht[len(linkText):len(ht)] + offsetLeft = len(linkText) + } + + // Make copies of parameter values + rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht + + // Link hints are in upper case in new tab mode + if vimMode == LinkModeNewTab { + htCopy = strings.ToUpper(htCopy) + } + + // Create closure + f := func() { + writeString(rLeftCopy+offsetLeft, rTopCopy+uiHeight, htCopy, tcell.StyleDefault) + } + fc = append(fc, &f) + } + // Create closure that calls the other closures + ff := func() { + for _, f := range fc { + (*f)() + } + } + linkHintWriteStringCalls = &ff +} + +func eraseLinkHints() { + linkText = "" + linkHintWriteStringCalls = nil + linkHintsToRects = make(map[string]*HintRect) + linkHintRects = nil +} + +func isNormalModeKey(ev *tcell.EventKey) bool { + if ev.Key() == tcell.KeyESC { + return true + } + return false +} + +func handleVimControl(ev *tcell.EventKey) { + var lastRune rune + command := "" + + if len(keyEvents) > 0 && keyEvents[0] != nil { + lastRune = keyEvents[len(keyEvents)-1].Rune() + } + + keyEvents = append(keyEvents, ev) + if len(keyEvents) > 10 { + keyEvents = keyEvents[1:] + } + + keyCombination := string(lastRune) + string(ev.Rune()) + + switch vimMode { + case WaitMode: + if time.Since(waitModeStartTime) < time.Millisecond*1000 { + return + } + vimMode = NormalMode + fallthrough + case NormalMode: + command = vimCommandsBindings[keyCombination] + if len(command) == 0 { + keyCombination := string(ev.Rune()) + command = vimCommandsBindings[keyCombination] + } + case InsertMode: + if isNormalModeKey(ev) { + command = "normalMode" + } + case VisualMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + if ev.Rune() == 'c' { + command = "caretMode" + } + if ev.Rune() == 'o' { + //swap cursor position begin->end or end->begin + } + if ev.Rune() == 'y' { + //clipboard + } + } + case CaretMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + switch ev.Key() { + case tcell.KeyEnter: + generateLeftClick(caretPos.X, caretPos.Y-uiHeight) + } + switch ev.Rune() { + case 'v': + command = "visualMode" + case 'h': + moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) + case 'l': + width, _ := screen.Size() + moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) + case 'k': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) + if caretPos.Y < uiHeight { + command = "scrollHalfPageUp" + if CurrentTab.frame.yScroll == 0 { + caretPos.Y = uiHeight + } else { + caretPos.Y += (height - uiHeight) / 2 + } + } + case 'j': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) + if caretPos.Y > height-uiHeight { + command = "scrollHalfPageDown" + caretPos.Y -= (height - uiHeight) / 2 + } + } + } + case MakeMarkMode: + if unicode.IsLower(ev.Rune()) { + if localMarkMap[CurrentTab.ID] == nil { + localMarkMap[CurrentTab.ID] = make(map[rune]*mark) + } + localMarkMap[CurrentTab.ID][ev.Rune()] = makeMark() + } else if unicode.IsUpper(ev.Rune()) { + globalMarkMap[ev.Rune()] = makeMark() + } + + command = "normalMode" + case GotoMarkMode: + if mark, ok := globalMarkMap[ev.Rune()]; ok { + gotoMark(mark) + } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { + if mark, ok := m[ev.Rune()]; ok { + gotoMark(mark) + } + } + + command = "normalMode" + case FindMode: + if isNormalModeKey(ev) { + command = "normalMode" + findText = "" + } else { + if ev.Key() == tcell.KeyEnter { + vimMode = NormalMode + command = "findText" + break + } + if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 { + if len(findText) > 0 { + findText = findText[:len(findText)-1] + } + } else { + findText += string(ev.Rune()) + } + } + case LinkMode, LinkModeNewTab, LinkModeCopy: + if isNormalModeKey(ev) { + command = "normalMode" + eraseLinkHints() + } else { + linkText += string(ev.Rune()) + updateLinkHintDisplay() + if linkModeWithHints { + if r, ok := linkHintsToRects[linkText]; ok { + if r != nil { + switch vimMode { + case LinkMode: + if (*r).Height == 2 { + generateLeftClickYHack((*r).Left, (*r).Top, true) + } else { + generateLeftClick((*r).Left, (*r).Top) + } + case LinkModeNewTab: + //generateMiddleClick(r.Left, r.Top) + sendMessageToWebExtension("/new_tab," + r.Href) + case LinkModeCopy: + clipboard.WriteAll(r.Href) + } + goIntoWaitMode() + eraseLinkHints() + } + } + } else { + coords := findAndHighlightTextOnScreen(linkText) + if len(coords) == 1 { + goIntoWaitMode() + + if vimMode == LinkModeNewTab { + generateMiddleClick(coords[0].X, coords[0].Y) + } else { + generateLeftClick(coords[0].X, coords[0].Y) + } + linkText = "" + return + } else if len(coords) == 0 { + vimMode = NormalMode + linkText = "" + return + } + } + } + } + + if len(command) > 0 { + executeVimCommand(command) + } +} + +func executeVimCommand(command string) { + switch command { + case "urlUp": + sendMessageToWebExtension("/tab_command,/url_up") + case "urlRoot": + sendMessageToWebExtension("/tab_command,/url_root") + case "scrollToTop": + doScroll(0, -CurrentTab.frame.domRowCount()) + case "scrollToBottom": + doScroll(0, CurrentTab.frame.domRowCount()) + case "scrollUp": + doScroll(0, -1) + case "scrollDown": + doScroll(0, 1) + case "scrollLeft": + doScroll(-1, 0) + case "scrollRight": + doScroll(1, 0) + case "editURL": + urlBarFocusToggle() + case "firstTab": + switchToTab(tabsOrder[0]) + case "lastTab": + switchToTab(tabsOrder[len(tabsOrder)-1]) + case "scrollHalfPageDown": + _, height := screen.Size() + doScroll(0, (height-uiHeight)/2) + case "scrollHalfPageUp": + _, height := screen.Size() + doScroll(0, -((height - uiHeight) / 2)) + case "historyBack": + sendMessageToWebExtension("/tab_command,/history_back") + case "historyForward": + sendMessageToWebExtension("/tab_command,/history_forward") + case "reload": + sendMessageToWebExtension("/tab_command,/reload") + case "prevTab": + prevTab() + case "nextTab": + nextTab() + case "previouslyVisitedTab": + previouslyVisitedTab() + case "newTab": + createNewEmptyTab() + case "removeTab": + removeTab(CurrentTab.ID) + case "restoreTab": + restoreTab() + case "duplicateTab": + duplicateTab(CurrentTab.ID) + case "moveTabLeft": + moveTabLeft(CurrentTab.ID) + case "moveTabRight": + moveTabRight(CurrentTab.ID) + case "copyURL": + clipboard.WriteAll(CurrentTab.URI) + case "openClipboardURL": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/tab_command,/url," + URI) + case "openClipboardURLInNewTab": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/new_tab," + URI) + case "focusFirstTextInput": + sendMessageToWebExtension("/tab_command,/focus_first_text_input") + case "followLinkLabeledNext": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_next") + case "followLinkLabeledPrevious": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_previous") + case "viewHelp": + sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") + case "openLinkInCurrentTab": + vimMode = LinkMode + sendMessageToWebExtension("/tab_command,/get_clickable_hints") + eraseLinkHints() + case "openLinkInNewTab": + vimMode = LinkModeNewTab + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "copyLinkURL": + vimMode = LinkModeCopy + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "findText": + fallthrough + case "findNext": + sendMessageToWebExtension("/tab_command,/find_next," + findText) + case "findPrevious": + sendMessageToWebExtension("/tab_command,/find_previous," + findText) + case "makeMark": + vimMode = MakeMarkMode + case "gotoMark": + vimMode = GotoMarkMode + case "insertMode": + vimMode = InsertMode + case "findMode": + vimMode = FindMode + case "normalMode": + vimMode = NormalMode + case "visualMode": + vimMode = VisualMode + case "caretMode": + vimMode = CaretMode + width, height := screen.Size() + caretPos.X, caretPos.Y = width/2, height/2 + } +} + +func doScroll(relX int, relY int) { + doScrollAbsolute(CurrentTab.frame.xScroll+relX, CurrentTab.frame.yScroll+relY) +} + +func doScrollAbsolute(absX int, absY int) { yScrollOriginal := CurrentTab.frame.yScroll _, height := screen.Size() height -= uiHeight + + CurrentTab.frame.yScroll = absY + CurrentTab.frame.xScroll = absX + + CurrentTab.frame.limitScroll(height) + sendMessageToWebExtension( + fmt.Sprintf("/tab_command,/scroll_status,%d,%d", + CurrentTab.frame.xScroll, CurrentTab.frame.yScroll*2)) + if CurrentTab.frame.yScroll != yScrollOriginal { + renderCurrentTabWindow() + } +} + +func handleScrolling(ev *tcell.EventKey) { + _, height := screen.Size() + height -= uiHeight if ev.Key() == tcell.KeyUp { - CurrentTab.frame.yScroll -= 2 + doScroll(0, -2) } if ev.Key() == tcell.KeyDown { - CurrentTab.frame.yScroll += 2 + doScroll(0, 2) } if ev.Key() == tcell.KeyPgUp { - CurrentTab.frame.yScroll -= height + doScroll(0, -height) } if ev.Key() == tcell.KeyPgDn { - CurrentTab.frame.yScroll += height - } - CurrentTab.frame.limitScroll(height) - sendMessageToWebExtension( - fmt.Sprintf( - "/tab_command,/scroll_status,%d,%d", - CurrentTab.frame.xScroll, - CurrentTab.frame.yScroll*2)) - if CurrentTab.frame.yScroll != yScrollOriginal { - renderCurrentTabWindow() + doScroll(0, height) } } -func handleMouseEvent(ev *tcell.EventMouse) { +func handleMouseEventYHack(ev *tcell.EventMouse, yHack bool) { if CurrentTab == nil { return } @@ -185,27 +723,24 @@ func handleMouseEvent(ev *tcell.EventMouse) { "mouse_y": int(yInFrame), "modifiers": int(ev.Modifiers()), } + if yHack { + eventMap["y_hack"] = true + } marshalled, _ := json.Marshal(eventMap) sendMessageToWebExtension("/stdin," + string(marshalled)) } +func handleMouseEvent(ev *tcell.EventMouse) { + handleMouseEventYHack(ev, false) +} + func handleMouseScroll(scrollType tcell.ButtonMask) { - yScrollOriginal := CurrentTab.frame.yScroll _, height := screen.Size() height -= uiHeight if scrollType == tcell.WheelUp { - CurrentTab.frame.yScroll -= 1 + doScroll(0, -1) } else if scrollType == tcell.WheelDown { - CurrentTab.frame.yScroll += 1 - } - CurrentTab.frame.limitScroll(height) - sendMessageToWebExtension( - fmt.Sprintf( - "/tab_command,/scroll_status,%d,%d", - CurrentTab.frame.xScroll, - CurrentTab.frame.yScroll*2)) - if CurrentTab.frame.yScroll != yScrollOriginal { - renderCurrentTabWindow() + doScroll(0, 1) } } @@ -254,10 +789,98 @@ func renderCurrentTabWindow() { activeInputBox.renderCursor() } overlayPageStatusMessage() + overlayVimMode() overlayCallToSupport() screen.Show() } +func searchScreenForText(text string) []Coordinate { + var offsets = make([]Coordinate, 0) + var splitString []string + var r rune + var s string + width, height := screen.Size() + screenText := "" + index := 0 + + for y := 0; y < height-uiHeight; y++ { + screenText = "" + for x := 0; x < width; x++ { + r = getCell(x, y).character[0] + s = string(r) + if len(s) == 0 || len(s) > 1 { + screenText += " " + } else { + screenText += string(getCell(x, y).character[0]) + } + } + index = 0 + splitString = strings.Split(strings.ToLower(screenText), strings.ToLower(text)) + for _, s := range splitString { + if index+len(s) >= width { + break + } + + offsets = append(offsets, Coordinate{index + len(s), y}) + index += len(s) + len(text) + } + } + return offsets +} + +func findAndHighlightTextOnScreen(text string) []Coordinate { + var x, y int + var styling = tcell.StyleDefault + + offsets := searchScreenForText(text) + for _, offset := range offsets { + y = offset.Y + x = offset.X + for z := 0; z < len(text); z++ { + screen.SetContent(x+z, y+uiHeight, rune(text[z]), nil, styling) + } + } + screen.Show() + return offsets +} + +// Parse incoming link hints +func parseJSONLinkHints(jsonString string) { + jsonBytes := []byte(jsonString) + if err := json.Unmarshal(jsonBytes, &linkHintRects); err != nil { + Shutdown(err) + } + + // Optimize link hint positions + for i := 0; i < len(linkHintRects); i++ { + r := &linkHintRects[i] + + // For links that are more than one line high + // we want to position the link hint in the vertical middle + if r.Height > 2 { + if r.Height%2 == 0 { + r.Top += r.Height / 2 + } else { + r.Top += r.Height/2 - 1 + } + } + + // For links that are more one character long we try to move + // the link hint two characters to the right, if possible. + if r.Width > 1 { + o := r.Left + r.Left += r.Width/2 - 1 + if r.Left > o+2 { + r.Left = o + 2 + } + } + } + + Log("Received parseJSONLinkHint") + // This is where the display of actual link hints is prepared + updateLinkHintDisplay() +} + func getCell(x, y int) cell { var currentCell cell var ok bool From 721b2c8e15273e9b32d9aa2cf7f484e9893501ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:20:44 -0400 Subject: [PATCH 15/77] Instead of adding 1 to Y coord, add 1 to height. This fixes an issues with activating input boxes using link hinting. --- interfacer/src/browsh/frame_builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index afb53c38..37f90eed 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -160,9 +160,9 @@ func (f *frame) updateInputBoxes(incoming incomingFrameText) { inputBox := f.inputBoxes[incomingInputBox.ID] inputBox.X = incomingInputBox.X // TODO: Why do we have to add the 1 to the y coord?? - inputBox.Y = (incomingInputBox.Y + 1) / 2 + inputBox.Y = (incomingInputBox.Y + 0) / 2 inputBox.Width = incomingInputBox.Width - inputBox.Height = incomingInputBox.Height / 2 + inputBox.Height = (incomingInputBox.Height / 2) + 1 inputBox.FgColour = incomingInputBox.FgColour inputBox.TagName = incomingInputBox.TagName inputBox.Type = incomingInputBox.Type From 61cd7e18183de1f66df90b39720ceba83692db8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:35:22 -0400 Subject: [PATCH 16/77] Prettified js files --- webext/src/background/tty_commands_mixin.js | 10 +- webext/src/vimium.js | 1686 ++++++++++++------- 2 files changed, 1060 insertions(+), 636 deletions(-) diff --git a/webext/src/background/tty_commands_mixin.js b/webext/src/background/tty_commands_mixin.js index 815674b6..dc33b59a 100644 --- a/webext/src/background/tty_commands_mixin.js +++ b/webext/src/background/tty_commands_mixin.js @@ -186,16 +186,16 @@ export default MixinBase => } restoreTab() { - var sessionsInfo = browser.sessions.getRecentlyClosed({maxResults: 1 }); + var sessionsInfo = browser.sessions.getRecentlyClosed({ maxResults: 1 }); sessionsInfo.then(this._restoreTab); } - + _restoreTab(sessionsInfo) { var mySessionInfo = sessionsInfo[0]; if (mySessionInfo.tab) { - browser.sessions.restore(mySessionInfo.tab.sessionId); - } else { - browser.sessions.restore(mySessionInfo.window.sessionId); + browser.sessions.restore(mySessionInfo.tab.sessionId); + } else { + browser.sessions.restore(mySessionInfo.window.sessionId); } } diff --git a/webext/src/vimium.js b/webext/src/vimium.js index 6463294f..246c5353 100644 --- a/webext/src/vimium.js +++ b/webext/src/vimium.js @@ -6,7 +6,7 @@ // to better fullfill the needs of Browsh: // // In line 11 of getLocalHints the following code was added: -// +// // if (requireHref && element.href && visibleElement.length > 0) { // visibleElement[0]["href"] = element.href; // } @@ -27,7 +27,7 @@ // The following lines just before the return statement in getLocalHints were // commented out, because we're currently not using this functionality and the // settings for it simply don't exist in Browsh (yet). -// +// // /*if (Settings.get("filterLinkHints")) { // for (m = 0, len3 = localHints.length; m < len3; m++) { // hint = localHints[m]; @@ -39,81 +39,114 @@ // because Browsh is currently assuming that it is being run in Firefox. var Rect = { - create: function(x1, y1, x2, y2) { - return { - bottom: y2, - top: y1, - left: x1, - right: x2, - width: x2 - x1, - height: y2 - y1 - }; - }, - copy: function(rect) { - return { - bottom: rect.bottom, - top: rect.top, - left: rect.left, - right: rect.right, - width: rect.width, - height: rect.height - }; - }, - translate: function(rect, x, y) { - if (x == null) { - x = 0; - } - if (y == null) { - y = 0; - } - return { - bottom: rect.bottom + y, - top: rect.top + y, - left: rect.left + x, - right: rect.right + x, - width: rect.width, - height: rect.height + create: function(x1, y1, x2, y2) { + return { + bottom: y2, + top: y1, + left: x1, + right: x2, + width: x2 - x1, + height: y2 - y1 }; - }, - subtract: function(rect1, rect2) { - var rects; - rect2 = this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); - if (rect2.width < 0 || rect2.height < 0) { - return [Rect.copy(rect1)]; - } - rects = [this.create(rect1.left, rect1.top, rect2.left, rect2.top), this.create(rect2.left, rect1.top, rect2.right, rect2.top), this.create(rect2.right, rect1.top, rect1.right, rect2.top), this.create(rect1.left, rect2.top, rect2.left, rect2.bottom), this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom)]; - return rects.filter(function(rect) { - return rect.height > 0 && rect.width > 0; - }); - }, - intersects: function(rect1, rect2) { - return rect1.right > rect2.left && rect1.left < rect2.right && rect1.bottom > rect2.top && rect1.top < rect2.bottom; - }, - intersectsStrict: function(rect1, rect2) { - return rect1.right >= rect2.left && rect1.left <= rect2.right && rect1.bottom >= rect2.top && rect1.top <= rect2.bottom; - }, - equals: function(rect1, rect2) { - var i, len, property, ref; - ref = ["top", "bottom", "left", "right", "width", "height"]; - for (i = 0, len = ref.length; i < len; i++) { - property = ref[i]; - if (rect1[property] !== rect2[property]) { - return false; - } + }, + copy: function(rect) { + return { + bottom: rect.bottom, + top: rect.top, + left: rect.left, + right: rect.right, + width: rect.width, + height: rect.height + }; + }, + translate: function(rect, x, y) { + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + return { + bottom: rect.bottom + y, + top: rect.top + y, + left: rect.left + x, + right: rect.right + x, + width: rect.width, + height: rect.height + }; + }, + subtract: function(rect1, rect2) { + var rects; + rect2 = this.create( + Math.max(rect1.left, rect2.left), + Math.max(rect1.top, rect2.top), + Math.min(rect1.right, rect2.right), + Math.min(rect1.bottom, rect2.bottom) + ); + if (rect2.width < 0 || rect2.height < 0) { + return [Rect.copy(rect1)]; + } + rects = [ + this.create(rect1.left, rect1.top, rect2.left, rect2.top), + this.create(rect2.left, rect1.top, rect2.right, rect2.top), + this.create(rect2.right, rect1.top, rect1.right, rect2.top), + this.create(rect1.left, rect2.top, rect2.left, rect2.bottom), + this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), + this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), + this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), + this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom) + ]; + return rects.filter(function(rect) { + return rect.height > 0 && rect.width > 0; + }); + }, + intersects: function(rect1, rect2) { + return ( + rect1.right > rect2.left && + rect1.left < rect2.right && + rect1.bottom > rect2.top && + rect1.top < rect2.bottom + ); + }, + intersectsStrict: function(rect1, rect2) { + return ( + rect1.right >= rect2.left && + rect1.left <= rect2.right && + rect1.bottom >= rect2.top && + rect1.top <= rect2.bottom + ); + }, + equals: function(rect1, rect2) { + var i, len, property, ref; + ref = ["top", "bottom", "left", "right", "width", "height"]; + for (i = 0, len = ref.length; i < len; i++) { + property = ref[i]; + if (rect1[property] !== rect2[property]) { + return false; } - return true; - }, - intersect: function(rect1, rect2) { - return this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); } - }; + return true; + }, + intersect: function(rect1, rect2) { + return this.create( + Math.max(rect1.left, rect2.left), + Math.max(rect1.top, rect2.top), + Math.min(rect1.right, rect2.right), + Math.min(rect1.bottom, rect2.bottom) + ); + } +}; var DomUtils = { - documentReady: function() { - var callbacks, isReady, onDOMContentLoaded, ref; - ref = [document.readyState !== "loading", []], isReady = ref[0], callbacks = ref[1]; - if (!isReady) { - window.addEventListener("DOMContentLoaded", onDOMContentLoaded = forTrusted(function() { + documentReady: function() { + var callbacks, isReady, onDOMContentLoaded, ref; + (ref = [document.readyState !== "loading", []]), + (isReady = ref[0]), + (callbacks = ref[1]); + if (!isReady) { + window.addEventListener( + "DOMContentLoaded", + (onDOMContentLoaded = forTrusted(function() { var callback, i, len; window.removeEventListener("DOMContentLoaded", onDOMContentLoaded); isReady = true; @@ -121,408 +154,696 @@ var DomUtils = { callback = callbacks[i]; callback(); } - return callbacks = null; - })); + return (callbacks = null); + })) + ); + } + return function(callback) { + if (isReady) { + return callback(); + } else { + return callbacks.push(callback); } - return function(callback) { - if (isReady) { - return callback(); - } else { - return callbacks.push(callback); - } - }; - }, - getVisibleClientRect: function(element, testChildren) { - var child, childClientRect, clientRect, clientRects, computedStyle, i, isInlineZeroHeight, j, len, len1, ref, ref1; - if (testChildren == null) { - testChildren = false; + }; + }, + getVisibleClientRect: function(element, testChildren) { + var child, + childClientRect, + clientRect, + clientRects, + computedStyle, + i, + isInlineZeroHeight, + j, + len, + len1, + ref, + ref1; + if (testChildren == null) { + testChildren = false; + } + clientRects = (function() { + var i, len, ref, results; + ref = element.getClientRects(); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + clientRect = ref[i]; + results.push(Rect.copy(clientRect)); } - clientRects = (function() { - var i, len, ref, results; - ref = element.getClientRects(); - results = []; - for (i = 0, len = ref.length; i < len; i++) { - clientRect = ref[i]; - results.push(Rect.copy(clientRect)); - } - return results; - })(); + return results; + })(); + isInlineZeroHeight = function() { + var elementComputedStyle, isInlineZeroFontSize; + elementComputedStyle = window.getComputedStyle(element, null); + isInlineZeroFontSize = + 0 === + elementComputedStyle.getPropertyValue("display").indexOf("inline") && + elementComputedStyle.getPropertyValue("font-size") === "0px"; isInlineZeroHeight = function() { - var elementComputedStyle, isInlineZeroFontSize; - elementComputedStyle = window.getComputedStyle(element, null); - isInlineZeroFontSize = (0 === elementComputedStyle.getPropertyValue("display").indexOf("inline")) && (elementComputedStyle.getPropertyValue("font-size") === "0px"); - isInlineZeroHeight = function() { - return isInlineZeroFontSize; - }; return isInlineZeroFontSize; }; - for (i = 0, len = clientRects.length; i < len; i++) { - clientRect = clientRects[i]; - if ((clientRect.width === 0 || clientRect.height === 0) && testChildren) { - ref = element.children; - for (j = 0, len1 = ref.length; j < len1; j++) { - child = ref[j]; - computedStyle = window.getComputedStyle(child, null); - if (computedStyle.getPropertyValue("float") === "none" && !((ref1 = computedStyle.getPropertyValue("position")) === "absolute" || ref1 === "fixed") && !(clientRect.height === 0 && isInlineZeroHeight() && 0 === computedStyle.getPropertyValue("display").indexOf("inline"))) { - continue; - } - childClientRect = this.getVisibleClientRect(child, true); - if (childClientRect === null || childClientRect.width < 3 || childClientRect.height < 3) { - continue; - } - return childClientRect; - } - } else { - clientRect = this.cropRectToVisible(clientRect); - if (clientRect === null || clientRect.width < 3 || clientRect.height < 3) { + return isInlineZeroFontSize; + }; + for (i = 0, len = clientRects.length; i < len; i++) { + clientRect = clientRects[i]; + if ((clientRect.width === 0 || clientRect.height === 0) && testChildren) { + ref = element.children; + for (j = 0, len1 = ref.length; j < len1; j++) { + child = ref[j]; + computedStyle = window.getComputedStyle(child, null); + if ( + computedStyle.getPropertyValue("float") === "none" && + !( + (ref1 = computedStyle.getPropertyValue("position")) === + "absolute" || ref1 === "fixed" + ) && + !( + clientRect.height === 0 && + isInlineZeroHeight() && + 0 === computedStyle.getPropertyValue("display").indexOf("inline") + ) + ) { continue; } - computedStyle = window.getComputedStyle(element, null); - if (computedStyle.getPropertyValue('visibility') !== 'visible') { + childClientRect = this.getVisibleClientRect(child, true); + if ( + childClientRect === null || + childClientRect.width < 3 || + childClientRect.height < 3 + ) { continue; } - return clientRect; + return childClientRect; } + } else { + clientRect = this.cropRectToVisible(clientRect); + if ( + clientRect === null || + clientRect.width < 3 || + clientRect.height < 3 + ) { + continue; + } + computedStyle = window.getComputedStyle(element, null); + if (computedStyle.getPropertyValue("visibility") !== "visible") { + continue; + } + return clientRect; } + } + return null; + }, + cropRectToVisible: function(rect) { + var boundedRect; + boundedRect = Rect.create( + Math.max(rect.left, 0), + Math.max(rect.top, 0), + rect.right, + rect.bottom + ); + if ( + boundedRect.top >= window.innerHeight - 4 || + boundedRect.left >= window.innerWidth - 4 + ) { return null; - }, - cropRectToVisible: function(rect) { - var boundedRect; - boundedRect = Rect.create(Math.max(rect.left, 0), Math.max(rect.top, 0), rect.right, rect.bottom); - if (boundedRect.top >= window.innerHeight - 4 || boundedRect.left >= window.innerWidth - 4) { - return null; + } else { + return boundedRect; + } + }, + getClientRectsForAreas: function(imgClientRect, areas) { + var area, + coords, + diff, + i, + len, + r, + rect, + rects, + ref, + shape, + x, + x1, + x2, + y, + y1, + y2; + rects = []; + for (i = 0, len = areas.length; i < len; i++) { + area = areas[i]; + coords = area.coords.split(",").map(function(coord) { + return parseInt(coord, 10); + }); + shape = area.shape.toLowerCase(); + if (shape === "rect" || shape === "rectangle") { + (x1 = coords[0]), (y1 = coords[1]), (x2 = coords[2]), (y2 = coords[3]); + } else if (shape === "circle" || shape === "circ") { + (x = coords[0]), (y = coords[1]), (r = coords[2]); + diff = r / Math.sqrt(2); + x1 = x - diff; + x2 = x + diff; + y1 = y - diff; + y2 = y + diff; + } else if (shape === "default") { + (ref = [0, 0, imgClientRect.width, imgClientRect.height]), + (x1 = ref[0]), + (y1 = ref[1]), + (x2 = ref[2]), + (y2 = ref[3]); } else { - return boundedRect; + (x1 = coords[0]), (y1 = coords[1]), (x2 = coords[2]), (y2 = coords[3]); } - }, - getClientRectsForAreas: function(imgClientRect, areas) { - var area, coords, diff, i, len, r, rect, rects, ref, shape, x, x1, x2, y, y1, y2; - rects = []; - for (i = 0, len = areas.length; i < len; i++) { - area = areas[i]; - coords = area.coords.split(",").map(function(coord) { - return parseInt(coord, 10); + rect = Rect.translate( + Rect.create(x1, y1, x2, y2), + imgClientRect.left, + imgClientRect.top + ); + rect = this.cropRectToVisible(rect); + if (rect && !isNaN(rect.top)) { + rects.push({ + element: area, + rect: rect }); - shape = area.shape.toLowerCase(); - if (shape === "rect" || shape === "rectangle") { - x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; - } else if (shape === "circle" || shape === "circ") { - x = coords[0], y = coords[1], r = coords[2]; - diff = r / Math.sqrt(2); - x1 = x - diff; - x2 = x + diff; - y1 = y - diff; - y2 = y + diff; - } else if (shape === "default") { - ref = [0, 0, imgClientRect.width, imgClientRect.height], x1 = ref[0], y1 = ref[1], x2 = ref[2], y2 = ref[3]; - } else { - x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; - } - rect = Rect.translate(Rect.create(x1, y1, x2, y2), imgClientRect.left, imgClientRect.top); - rect = this.cropRectToVisible(rect); - if (rect && !isNaN(rect.top)) { - rects.push({ - element: area, - rect: rect - }); - } } - return rects; - }, - isSelectable: function(element) { - var unselectableTypes; - if (!(element instanceof Element)) { - return false; + } + return rects; + }, + isSelectable: function(element) { + var unselectableTypes; + if (!(element instanceof Element)) { + return false; + } + unselectableTypes = [ + "button", + "checkbox", + "color", + "file", + "hidden", + "image", + "radio", + "reset", + "submit" + ]; + return ( + (element.nodeName.toLowerCase() === "input" && + unselectableTypes.indexOf(element.type) === -1) || + element.nodeName.toLowerCase() === "textarea" || + element.isContentEditable + ); + }, + getViewportTopLeft: function() { + var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; + box = document.documentElement; + style = getComputedStyle(box); + rect = box.getBoundingClientRect(); + if ( + style.position === "static" && + !/content|paint|strict/.test(style.contain || "") + ) { + marginTop = parseInt(style.marginTop); + marginLeft = parseInt(style.marginLeft); + return { + top: -rect.top + marginTop, + left: -rect.left + marginLeft + }; + } else { + //if (Utils.isFirefox()) + if (true) { + clientTop = parseInt(style.borderTopWidth); + clientLeft = parseInt(style.borderLeftWidth); + } else { + (clientTop = box.clientTop), (clientLeft = box.clientLeft); } - unselectableTypes = ["button", "checkbox", "color", "file", "hidden", "image", "radio", "reset", "submit"]; - return (element.nodeName.toLowerCase() === "input" && unselectableTypes.indexOf(element.type) === -1) || element.nodeName.toLowerCase() === "textarea" || element.isContentEditable; - }, - getViewportTopLeft: function() { - var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; - box = document.documentElement; - style = getComputedStyle(box); - rect = box.getBoundingClientRect(); - if (style.position === "static" && !/content|paint|strict/.test(style.contain || "")) { - marginTop = parseInt(style.marginTop); - marginLeft = parseInt(style.marginLeft); - return { - top: -rect.top + marginTop, - left: -rect.left + marginLeft - }; + return { + top: -rect.top - clientTop, + left: -rect.left - clientLeft + }; + } + }, + makeXPath: function(elementArray) { + var element, i, len, xpath; + xpath = []; + for (i = 0, len = elementArray.length; i < len; i++) { + element = elementArray[i]; + xpath.push(".//" + element, ".//xhtml:" + element); + } + return xpath.join(" | "); + }, + evaluateXPath: function(xpath, resultType) { + var contextNode, namespaceResolver; + contextNode = document.webkitIsFullScreen + ? document.webkitFullscreenElement + : document.documentElement; + namespaceResolver = function(namespace) { + if (namespace === "xhtml") { + return "http://www.w3.org/1999/xhtml"; } else { - //if (Utils.isFirefox()) - if (true) { - clientTop = parseInt(style.borderTopWidth); - clientLeft = parseInt(style.borderLeftWidth); - } else { - clientTop = box.clientTop, clientLeft = box.clientLeft; - } - return { - top: -rect.top - clientTop, - left: -rect.left - clientLeft - }; + return null; } - }, - makeXPath: function(elementArray) { - var element, i, len, xpath; - xpath = []; - for (i = 0, len = elementArray.length; i < len; i++) { - element = elementArray[i]; - xpath.push(".//" + element, ".//xhtml:" + element); - } - return xpath.join(" | "); - }, - evaluateXPath: function(xpath, resultType) { - var contextNode, namespaceResolver; - contextNode = document.webkitIsFullScreen ? document.webkitFullscreenElement : document.documentElement; - namespaceResolver = function(namespace) { - if (namespace === "xhtml") { - return "http://www.w3.org/1999/xhtml"; - } else { - return null; - } - }; - return document.evaluate(xpath, contextNode, namespaceResolver, resultType, null); - }, - simulateClick: function(element, modifiers) { - var defaultActionShouldTrigger, event, eventSequence, i, len, results; - if (modifiers == null) { - modifiers = {}; - } - eventSequence = ["mouseover", "mousedown", "mouseup", "click"]; - results = []; - for (i = 0, len = eventSequence.length; i < len; i++) { - event = eventSequence[i]; - defaultActionShouldTrigger = /*Utils.isFirefox() &&*/ Object.keys(modifiers).length === 0 && event === "click" && element.target === "_blank" && element.href && !element.hasAttribute("onclick") && !element.hasAttribute("_vimium-has-onclick-listener") ? true : this.simulateMouseEvent(event, element, modifiers); - if (event === "click" && defaultActionShouldTrigger /*&& Utils.isFirefox()*/) { - if (0 < Object.keys(modifiers).length || element.target === "_blank") { - DomUtils.simulateClickDefaultAction(element, modifiers); - } - } - results.push(defaultActionShouldTrigger); - } - return results; - }, - simulateMouseEvent: (function() { - var lastHoveredElement; - lastHoveredElement = void 0; - return function(event, element, modifiers) { - var mouseEvent; - if (modifiers == null) { - modifiers = {}; - } - if (event === "mouseout") { - if (element == null) { - element = lastHoveredElement; - } - lastHoveredElement = void 0; - if (element == null) { - return; - } - } else if (event === "mouseover") { - this.simulateMouseEvent("mouseout", void 0, modifiers); - lastHoveredElement = element; - } - mouseEvent = document.createEvent("MouseEvents"); - mouseEvent.initMouseEvent(event, true, true, window, 1, 0, 0, 0, 0, modifiers.ctrlKey, modifiers.altKey, modifiers.shiftKey, modifiers.metaKey, 0, null); - return element.dispatchEvent(mouseEvent); - }; - })(), - simulateClickDefaultAction: function(element, modifiers) { - var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; - if (modifiers == null) { - modifiers = {}; - } - if (!(((ref = element.tagName) != null ? ref.toLowerCase() : void 0) === "a" && (element.href != null))) { - return; - } - ctrlKey = modifiers.ctrlKey, shiftKey = modifiers.shiftKey, metaKey = modifiers.metaKey, altKey = modifiers.altKey; - if (KeyboardUtils.platform === "Mac") { - newTabModifier = metaKey === true && ctrlKey === false; - } else { - newTabModifier = metaKey === false && ctrlKey === true; - } - if (newTabModifier) { - chrome.runtime.sendMessage({ - handler: "openUrlInNewTab", - url: element.href, - active: shiftKey === true - }); - } else if (shiftKey === true && metaKey === false && ctrlKey === false && altKey === false) { - chrome.runtime.sendMessage({ - handler: "openUrlInNewWindow", - url: element.href - }); - } else if (element.target === "_blank") { - chrome.runtime.sendMessage({ - handler: "openUrlInNewTab", - url: element.href, - active: true - }); - } - } -} - - var LocalHints = { - getVisibleClickable: function(element) { - var actionName, areas, areasAndRects, base1, clientRect, contentEditable, eventType, i, imgClientRects, isClickable, jsactionRule, jsactionRules, len, map, mapName, namespace, onlyHasTabIndex, possibleFalsePositive, reason, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, role, ruleSplit, tabIndex, tabIndexValue, tagName, visibleElements, slice; - tagName = (ref = typeof (base1 = element.tagName).toLowerCase === "function" ? base1.toLowerCase() : void 0) != null ? ref : ""; - isClickable = false; - onlyHasTabIndex = false; - possibleFalsePositive = false; - visibleElements = []; - reason = null; - slice = [].slice; - if (tagName === "img") { - mapName = element.getAttribute("usemap"); - if (mapName) { - imgClientRects = element.getClientRects(); - mapName = mapName.replace(/^#/, "").replace("\"", "\\\""); - map = document.querySelector("map[name=\"" + mapName + "\"]"); - if (map && imgClientRects.length > 0) { - areas = map.getElementsByTagName("area"); - areasAndRects = DomUtils.getClientRectsForAreas(imgClientRects[0], areas); - visibleElements.push.apply(visibleElements, areasAndRects); - } + }; + return document.evaluate( + xpath, + contextNode, + namespaceResolver, + resultType, + null + ); + }, + simulateClick: function(element, modifiers) { + var defaultActionShouldTrigger, event, eventSequence, i, len, results; + if (modifiers == null) { + modifiers = {}; + } + eventSequence = ["mouseover", "mousedown", "mouseup", "click"]; + results = []; + for (i = 0, len = eventSequence.length; i < len; i++) { + event = eventSequence[i]; + defaultActionShouldTrigger = + /*Utils.isFirefox() &&*/ Object.keys(modifiers).length === 0 && + event === "click" && + element.target === "_blank" && + element.href && + !element.hasAttribute("onclick") && + !element.hasAttribute("_vimium-has-onclick-listener") + ? true + : this.simulateMouseEvent(event, element, modifiers); + if ( + event === "click" && + defaultActionShouldTrigger /*&& Utils.isFirefox()*/ + ) { + if (0 < Object.keys(modifiers).length || element.target === "_blank") { + DomUtils.simulateClickDefaultAction(element, modifiers); } } - if (((ref1 = (ref2 = element.getAttribute("aria-hidden")) != null ? ref2.toLowerCase() : void 0) === "" || ref1 === "true") || ((ref3 = (ref4 = element.getAttribute("aria-disabled")) != null ? ref4.toLowerCase() : void 0) === "" || ref3 === "true")) { - return []; + results.push(defaultActionShouldTrigger); + } + return results; + }, + simulateMouseEvent: (function() { + var lastHoveredElement; + lastHoveredElement = void 0; + return function(event, element, modifiers) { + var mouseEvent; + if (modifiers == null) { + modifiers = {}; } - if (this.checkForAngularJs == null) { - this.checkForAngularJs = (function() { - var angularElements, i, k, len, len1, ngAttributes, prefix, ref5, ref6, separator; - angularElements = document.getElementsByClassName("ng-scope"); - if (angularElements.length === 0) { - return function() { - return false; - }; - } else { - ngAttributes = []; - ref5 = ['', 'data-', 'x-']; - for (i = 0, len = ref5.length; i < len; i++) { - prefix = ref5[i]; - ref6 = ['-', ':', '_']; - for (k = 0, len1 = ref6.length; k < len1; k++) { - separator = ref6[k]; - ngAttributes.push(prefix + "ng" + separator + "click"); - } - } - return function(element) { - var attribute, l, len2; - for (l = 0, len2 = ngAttributes.length; l < len2; l++) { - attribute = ngAttributes[l]; - if (element.hasAttribute(attribute)) { - return true; - } - } - return false; - }; - } - })(); + if (event === "mouseout") { + if (element == null) { + element = lastHoveredElement; + } + lastHoveredElement = void 0; + if (element == null) { + return; + } + } else if (event === "mouseover") { + this.simulateMouseEvent("mouseout", void 0, modifiers); + lastHoveredElement = element; } - isClickable || (isClickable = this.checkForAngularJs(element)); - if (element.hasAttribute("onclick") || (role = element.getAttribute("role")) && ((ref5 = role.toLowerCase()) === "button" || ref5 === "tab" || ref5 === "link" || ref5 === "checkbox" || ref5 === "menuitem" || ref5 === "menuitemcheckbox" || ref5 === "menuitemradio") || (contentEditable = element.getAttribute("contentEditable")) && ((ref6 = contentEditable.toLowerCase()) === "" || ref6 === "contenteditable" || ref6 === "true")) { - isClickable = true; + mouseEvent = document.createEvent("MouseEvents"); + mouseEvent.initMouseEvent( + event, + true, + true, + window, + 1, + 0, + 0, + 0, + 0, + modifiers.ctrlKey, + modifiers.altKey, + modifiers.shiftKey, + modifiers.metaKey, + 0, + null + ); + return element.dispatchEvent(mouseEvent); + }; + })(), + simulateClickDefaultAction: function(element, modifiers) { + var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; + if (modifiers == null) { + modifiers = {}; + } + if ( + !( + ((ref = element.tagName) != null ? ref.toLowerCase() : void 0) === + "a" && element.href != null + ) + ) { + return; + } + (ctrlKey = modifiers.ctrlKey), + (shiftKey = modifiers.shiftKey), + (metaKey = modifiers.metaKey), + (altKey = modifiers.altKey); + if (KeyboardUtils.platform === "Mac") { + newTabModifier = metaKey === true && ctrlKey === false; + } else { + newTabModifier = metaKey === false && ctrlKey === true; + } + if (newTabModifier) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: shiftKey === true + }); + } else if ( + shiftKey === true && + metaKey === false && + ctrlKey === false && + altKey === false + ) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewWindow", + url: element.href + }); + } else if (element.target === "_blank") { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: true + }); + } + } +}; + +var LocalHints = { + getVisibleClickable: function(element) { + var actionName, + areas, + areasAndRects, + base1, + clientRect, + contentEditable, + eventType, + i, + imgClientRects, + isClickable, + jsactionRule, + jsactionRules, + len, + map, + mapName, + namespace, + onlyHasTabIndex, + possibleFalsePositive, + reason, + ref, + ref1, + ref10, + ref11, + ref12, + ref2, + ref3, + ref4, + ref5, + ref6, + ref7, + ref8, + ref9, + role, + ruleSplit, + tabIndex, + tabIndexValue, + tagName, + visibleElements, + slice; + tagName = + (ref = + typeof (base1 = element.tagName).toLowerCase === "function" + ? base1.toLowerCase() + : void 0) != null + ? ref + : ""; + isClickable = false; + onlyHasTabIndex = false; + possibleFalsePositive = false; + visibleElements = []; + reason = null; + slice = [].slice; + if (tagName === "img") { + mapName = element.getAttribute("usemap"); + if (mapName) { + imgClientRects = element.getClientRects(); + mapName = mapName.replace(/^#/, "").replace('"', '\\"'); + map = document.querySelector('map[name="' + mapName + '"]'); + if (map && imgClientRects.length > 0) { + areas = map.getElementsByTagName("area"); + areasAndRects = DomUtils.getClientRectsForAreas( + imgClientRects[0], + areas + ); + visibleElements.push.apply(visibleElements, areasAndRects); + } } - if (!isClickable && element.hasAttribute("jsaction")) { - jsactionRules = element.getAttribute("jsaction").split(";"); - for (i = 0, len = jsactionRules.length; i < len; i++) { - jsactionRule = jsactionRules[i]; - ruleSplit = jsactionRule.trim().split(":"); - if ((1 <= (ref7 = ruleSplit.length) && ref7 <= 2)) { - ref8 = ruleSplit.length === 1 ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), ["_"]) : [ruleSplit[0]].concat(slice.call(ruleSplit[1].trim().split(".")), ["_"]), eventType = ref8[0], namespace = ref8[1], actionName = ref8[2]; - isClickable || (isClickable = eventType === "click" && namespace !== "none" && actionName !== "_"); + } + if ( + (ref1 = + (ref2 = element.getAttribute("aria-hidden")) != null + ? ref2.toLowerCase() + : void 0) === "" || + ref1 === "true" || + ((ref3 = + (ref4 = element.getAttribute("aria-disabled")) != null + ? ref4.toLowerCase() + : void 0) === "" || + ref3 === "true") + ) { + return []; + } + if (this.checkForAngularJs == null) { + this.checkForAngularJs = (function() { + var angularElements, + i, + k, + len, + len1, + ngAttributes, + prefix, + ref5, + ref6, + separator; + angularElements = document.getElementsByClassName("ng-scope"); + if (angularElements.length === 0) { + return function() { + return false; + }; + } else { + ngAttributes = []; + ref5 = ["", "data-", "x-"]; + for (i = 0, len = ref5.length; i < len; i++) { + prefix = ref5[i]; + ref6 = ["-", ":", "_"]; + for (k = 0, len1 = ref6.length; k < len1; k++) { + separator = ref6[k]; + ngAttributes.push(prefix + "ng" + separator + "click"); + } } + return function(element) { + var attribute, l, len2; + for (l = 0, len2 = ngAttributes.length; l < len2; l++) { + attribute = ngAttributes[l]; + if (element.hasAttribute(attribute)) { + return true; + } + } + return false; + }; } - } - switch (tagName) { - case "a": - isClickable = true; - break; - case "textarea": - isClickable || (isClickable = !element.disabled && !element.readOnly); - break; - case "input": - isClickable || (isClickable = !(((ref9 = element.getAttribute("type")) != null ? ref9.toLowerCase() : void 0) === "hidden" || element.disabled || (element.readOnly && DomUtils.isSelectable(element)))); - break; - case "button": - case "select": - isClickable || (isClickable = !element.disabled); - break; - case "label": - isClickable || (isClickable = (element.control != null) && !element.control.disabled && (this.getVisibleClickable(element.control)).length === 0); - break; - case "body": - isClickable || (isClickable = element === document.body && !windowIsFocused() && window.innerWidth > 3 && window.innerHeight > 3 && ((ref10 = document.body) != null ? ref10.tagName.toLowerCase() : void 0) !== "frameset" ? reason = "Frame." : void 0); - //isClickable || (isClickable = element === document.body && windowIsFocused() && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); - break; - case "img": - isClickable || (isClickable = (ref11 = element.style.cursor) === "zoom-in" || ref11 === "zoom-out"); - break; - case "div": - case "ol": - case "ul": - //isClickable || (isClickable = element.clientHeight < element.scrollHeight && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); - break; - case "details": - isClickable = true; - reason = "Open."; - } - if (!isClickable && 0 <= ((ref12 = element.getAttribute("class")) != null ? ref12.toLowerCase().indexOf("button") : void 0)) { - possibleFalsePositive = isClickable = true; - } - tabIndexValue = element.getAttribute("tabindex"); - tabIndex = tabIndexValue === "" ? 0 : parseInt(tabIndexValue); - if (!(isClickable || isNaN(tabIndex) || tabIndex < 0)) { - isClickable = onlyHasTabIndex = true; - } - if (isClickable) { - clientRect = DomUtils.getVisibleClientRect(element, true); - if (clientRect !== null) { - visibleElements.push({ - element: element, - rect: clientRect, - secondClassCitizen: onlyHasTabIndex, - possibleFalsePositive: possibleFalsePositive, - reason: reason - }); + })(); + } + isClickable || (isClickable = this.checkForAngularJs(element)); + if ( + element.hasAttribute("onclick") || + ((role = element.getAttribute("role")) && + ((ref5 = role.toLowerCase()) === "button" || + ref5 === "tab" || + ref5 === "link" || + ref5 === "checkbox" || + ref5 === "menuitem" || + ref5 === "menuitemcheckbox" || + ref5 === "menuitemradio")) || + ((contentEditable = element.getAttribute("contentEditable")) && + ((ref6 = contentEditable.toLowerCase()) === "" || + ref6 === "contenteditable" || + ref6 === "true")) + ) { + isClickable = true; + } + if (!isClickable && element.hasAttribute("jsaction")) { + jsactionRules = element.getAttribute("jsaction").split(";"); + for (i = 0, len = jsactionRules.length; i < len; i++) { + jsactionRule = jsactionRules[i]; + ruleSplit = jsactionRule.trim().split(":"); + if (1 <= (ref7 = ruleSplit.length) && ref7 <= 2) { + (ref8 = + ruleSplit.length === 1 + ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), [ + "_" + ]) + : [ruleSplit[0]].concat( + slice.call(ruleSplit[1].trim().split(".")), + ["_"] + )), + (eventType = ref8[0]), + (namespace = ref8[1]), + (actionName = ref8[2]); + isClickable || + (isClickable = + eventType === "click" && + namespace !== "none" && + actionName !== "_"); } } - return visibleElements; - }, - getLocalHints: function(requireHref) { - var descendantsToCheck, element, elements, hint, i, k, l, left, len, len1, len2, len3, localHints, m, negativeRect, nonOverlappingElements, position, rects, ref, ref1, top, visibleElement, visibleElements; - if (!document.documentElement) { - return []; + } + switch (tagName) { + case "a": + isClickable = true; + break; + case "textarea": + isClickable || (isClickable = !element.disabled && !element.readOnly); + break; + case "input": + isClickable || + (isClickable = !( + ((ref9 = element.getAttribute("type")) != null + ? ref9.toLowerCase() + : void 0) === "hidden" || + element.disabled || + (element.readOnly && DomUtils.isSelectable(element)) + )); + break; + case "button": + case "select": + isClickable || (isClickable = !element.disabled); + break; + case "label": + isClickable || + (isClickable = + element.control != null && + !element.control.disabled && + this.getVisibleClickable(element.control).length === 0); + break; + case "body": + isClickable || + (isClickable = + element === document.body && + !windowIsFocused() && + window.innerWidth > 3 && + window.innerHeight > 3 && + ((ref10 = document.body) != null + ? ref10.tagName.toLowerCase() + : void 0) !== "frameset" + ? (reason = "Frame.") + : void 0); + //isClickable || (isClickable = element === document.body && windowIsFocused() && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "img": + isClickable || + (isClickable = + (ref11 = element.style.cursor) === "zoom-in" || + ref11 === "zoom-out"); + break; + case "div": + case "ol": + case "ul": + //isClickable || (isClickable = element.clientHeight < element.scrollHeight && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "details": + isClickable = true; + reason = "Open."; + } + if ( + !isClickable && + 0 <= + ((ref12 = element.getAttribute("class")) != null + ? ref12.toLowerCase().indexOf("button") + : void 0) + ) { + possibleFalsePositive = isClickable = true; + } + tabIndexValue = element.getAttribute("tabindex"); + tabIndex = tabIndexValue === "" ? 0 : parseInt(tabIndexValue); + if (!(isClickable || isNaN(tabIndex) || tabIndex < 0)) { + isClickable = onlyHasTabIndex = true; + } + if (isClickable) { + clientRect = DomUtils.getVisibleClientRect(element, true); + if (clientRect !== null) { + visibleElements.push({ + element: element, + rect: clientRect, + secondClassCitizen: onlyHasTabIndex, + possibleFalsePositive: possibleFalsePositive, + reason: reason + }); } - elements = document.documentElement.getElementsByTagName("*"); - visibleElements = []; - for (i = 0, len = elements.length; i < len; i++) { - element = elements[i]; - if (!(requireHref && !element.href)) { - visibleElement = this.getVisibleClickable(element); - if (requireHref && element.href && visibleElement.length > 0) { - visibleElement[0]["href"] = element.href; - } - visibleElements.push.apply(visibleElements, visibleElement); + } + return visibleElements; + }, + getLocalHints: function(requireHref) { + var descendantsToCheck, + element, + elements, + hint, + i, + k, + l, + left, + len, + len1, + len2, + len3, + localHints, + m, + negativeRect, + nonOverlappingElements, + position, + rects, + ref, + ref1, + top, + visibleElement, + visibleElements; + if (!document.documentElement) { + return []; + } + elements = document.documentElement.getElementsByTagName("*"); + visibleElements = []; + for (i = 0, len = elements.length; i < len; i++) { + element = elements[i]; + if (!(requireHref && !element.href)) { + visibleElement = this.getVisibleClickable(element); + if (requireHref && element.href && visibleElement.length > 0) { + visibleElement[0]["href"] = element.href; } + visibleElements.push.apply(visibleElements, visibleElement); } - visibleElements = visibleElements.reverse(); - descendantsToCheck = [1, 2, 3]; - visibleElements = (function() { - var k, len1, results; - results = []; - for (position = k = 0, len1 = visibleElements.length; k < len1; position = ++k) { - element = visibleElements[position]; - if (element.possibleFalsePositive && (function() { + } + visibleElements = visibleElements.reverse(); + descendantsToCheck = [1, 2, 3]; + visibleElements = (function() { + var k, len1, results; + results = []; + for ( + position = k = 0, len1 = visibleElements.length; + k < len1; + position = ++k + ) { + element = visibleElements[position]; + if ( + element.possibleFalsePositive && + (function() { var _, candidateDescendant, index, l, len2; index = Math.max(0, position - 6); while (index < position) { candidateDescendant = visibleElements[index].element; for (l = 0, len2 = descendantsToCheck.length; l < len2; l++) { _ = descendantsToCheck[l]; - candidateDescendant = candidateDescendant != null ? candidateDescendant.parentElement : void 0; + candidateDescendant = + candidateDescendant != null + ? candidateDescendant.parentElement + : void 0; if (candidateDescendant === element.element) { return true; } @@ -530,192 +851,288 @@ var DomUtils = { index += 1; } return false; - })()) { - continue; - } - results.push(element); + })() + ) { + continue; } - return results; - })(); - localHints = nonOverlappingElements = []; - while (visibleElement = visibleElements.pop()) { - rects = [visibleElement.rect]; - for (k = 0, len1 = visibleElements.length; k < len1; k++) { - negativeRect = visibleElements[k].rect; - rects = (ref = []).concat.apply(ref, rects.map(function(rect) { + results.push(element); + } + return results; + })(); + localHints = nonOverlappingElements = []; + while ((visibleElement = visibleElements.pop())) { + rects = [visibleElement.rect]; + for (k = 0, len1 = visibleElements.length; k < len1; k++) { + negativeRect = visibleElements[k].rect; + rects = (ref = []).concat.apply( + ref, + rects.map(function(rect) { return Rect.subtract(rect, negativeRect); - })); + }) + ); + } + if (rects.length > 0) { + try { + var testextend = extend(visibleElement, { rect: rects[0] }); + nonOverlappingElements.push(testextend); + } catch (error) { + nonOverlappingElements.push(visibleElement); } - if (rects.length > 0) { - try { - var testextend = extend(visibleElement, { rect: rects[0] }); - nonOverlappingElements.push(testextend); - } catch(error) { - nonOverlappingElements.push(visibleElement); - } - /*nonOverlappingElements.push(extend(visibleElement, { + /*nonOverlappingElements.push(extend(visibleElement, { rect: rects[0] }));*/ - } else { - if (!visibleElement.secondClassCitizen) { - nonOverlappingElements.push(visibleElement); - } + } else { + if (!visibleElement.secondClassCitizen) { + nonOverlappingElements.push(visibleElement); } - } - ref1 = DomUtils.getViewportTopLeft(), top = ref1.top, left = ref1.left; - for (l = 0, len2 = nonOverlappingElements.length; l < len2; l++) { - hint = nonOverlappingElements[l]; - hint.rect.top += top; - hint.rect.left += left; } - /*if (Settings.get("filterLinkHints")) { + } + (ref1 = DomUtils.getViewportTopLeft()), + (top = ref1.top), + (left = ref1.left); + for (l = 0, len2 = nonOverlappingElements.length; l < len2; l++) { + hint = nonOverlappingElements[l]; + hint.rect.top += top; + hint.rect.left += left; + } + /*if (Settings.get("filterLinkHints")) { for (m = 0, len3 = localHints.length; m < len3; m++) { hint = localHints[m]; extend(hint, this.generateLinkText(hint)); } }*/ - return localHints; - }, - generateLinkText: function(hint) { - var element, linkText, nodeName, ref, showLinkText; - element = hint.element; - linkText = ""; - showLinkText = false; - nodeName = element.nodeName.toLowerCase(); - if (nodeName === "input") { - if ((element.labels != null) && element.labels.length > 0) { - linkText = element.labels[0].textContent.trim(); - if (linkText[linkText.length - 1] === ":") { - linkText = linkText.slice(0, linkText.length - 1); - } - showLinkText = true; - } else if (((ref = element.getAttribute("type")) != null ? ref.toLowerCase() : void 0) === "file") { - linkText = "Choose File"; - } else if (element.type !== "password") { - linkText = element.value; - if (!linkText && 'placeholder' in element) { - linkText = element.placeholder; - } + return localHints; + }, + generateLinkText: function(hint) { + var element, linkText, nodeName, ref, showLinkText; + element = hint.element; + linkText = ""; + showLinkText = false; + nodeName = element.nodeName.toLowerCase(); + if (nodeName === "input") { + if (element.labels != null && element.labels.length > 0) { + linkText = element.labels[0].textContent.trim(); + if (linkText[linkText.length - 1] === ":") { + linkText = linkText.slice(0, linkText.length - 1); } - } else if (nodeName === "a" && !element.textContent.trim() && element.firstElementChild && element.firstElementChild.nodeName.toLowerCase() === "img") { - linkText = element.firstElementChild.alt || element.firstElementChild.title; - if (linkText) { - showLinkText = true; + showLinkText = true; + } else if ( + ((ref = element.getAttribute("type")) != null + ? ref.toLowerCase() + : void 0) === "file" + ) { + linkText = "Choose File"; + } else if (element.type !== "password") { + linkText = element.value; + if (!linkText && "placeholder" in element) { + linkText = element.placeholder; } - } else if (hint.reason != null) { - linkText = hint.reason; + } + } else if ( + nodeName === "a" && + !element.textContent.trim() && + element.firstElementChild && + element.firstElementChild.nodeName.toLowerCase() === "img" + ) { + linkText = + element.firstElementChild.alt || element.firstElementChild.title; + if (linkText) { showLinkText = true; - } else if (0 < element.textContent.length) { - linkText = element.textContent.slice(0, 256); - } else if (element.hasAttribute("title")) { - linkText = element.getAttribute("title"); - } else { - linkText = element.innerHTML.slice(0, 256); } - return { - linkText: linkText.trim(), - showLinkText: showLinkText - }; + } else if (hint.reason != null) { + linkText = hint.reason; + showLinkText = true; + } else if (0 < element.textContent.length) { + linkText = element.textContent.slice(0, 256); + } else if (element.hasAttribute("title")) { + linkText = element.getAttribute("title"); + } else { + linkText = element.innerHTML.slice(0, 256); } - }; + return { + linkText: linkText.trim(), + showLinkText: showLinkText + }; + } +}; var VimiumNormal = { - followLink : function(linkElement) { - if (linkElement.nodeName.toLowerCase() === "link") { - return window.location.href = linkElement.href; - } else { - linkElement.scrollIntoView(); - return DomUtils.simulateClick(linkElement); - } - }, - findAndFollowLink : function(linkStrings) { - var boundingClientRect, candidateLink, candidateLinks, computedStyle, exactWordRegex, i, j, k, l, len, len1, len2, len3, link, linkMatches, linkString, links, linksXPath, m, n, ref, ref1; - linksXPath = DomUtils.makeXPath(["a", "*[@onclick or @role='link' or contains(@class, 'button')]"]); - links = DomUtils.evaluateXPath(linksXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - candidateLinks = []; - for (i = j = ref = links.snapshotLength - 1; j >= 0; i = j += -1) { - link = links.snapshotItem(i); - boundingClientRect = link.getBoundingClientRect(); - if (boundingClientRect.width === 0 || boundingClientRect.height === 0) { - continue; - } - computedStyle = window.getComputedStyle(link, null); - if (computedStyle.getPropertyValue("visibility") !== "visible" || computedStyle.getPropertyValue("display") === "none") { - continue; - } - linkMatches = false; - for (k = 0, len = linkStrings.length; k < len; k++) { - linkString = linkStrings[k]; - if (link.innerText.toLowerCase().indexOf(linkString) !== -1 || 0 <= ((ref1 = link.value) != null ? typeof ref1.indexOf === "function" ? ref1.indexOf(linkString) : void 0 : void 0)) { - linkMatches = true; - break; - } - } - if (!linkMatches) { - continue; - } - candidateLinks.push(link); - } - if (candidateLinks.length === 0) { - return; - } - for (l = 0, len1 = candidateLinks.length; l < len1; l++) { - link = candidateLinks[l]; - link.wordCount = link.innerText.trim().split(/\s+/).length; - } - candidateLinks.forEach(function(a, i) { - return a.originalIndex = i; - }); - candidateLinks = candidateLinks.sort(function(a, b) { - if (a.wordCount === b.wordCount) { - return a.originalIndex - b.originalIndex; - } else { - return a.wordCount - b.wordCount; - } - }).filter(function(a) { - return a.wordCount <= candidateLinks[0].wordCount + 1; - }); - for (m = 0, len2 = linkStrings.length; m < len2; m++) { - linkString = linkStrings[m]; - exactWordRegex = /\b/.test(linkString[0]) || /\b/.test(linkString[linkString.length - 1]) ? new RegExp("\\b" + linkString + "\\b", "i") : new RegExp(linkString, "i"); - for (n = 0, len3 = candidateLinks.length; n < len3; n++) { - candidateLink = candidateLinks[n]; - if (exactWordRegex.test(candidateLink.innerText) || (candidateLink.value && exactWordRegex.test(candidateLink.value))) { - this.followLink(candidateLink); - return true; - } - } - } - return false; - }, - findAndFollowRel : function(value) { - var element, elements, j, k, len, len1, relTags, tag; - relTags = ["link", "a", "area"]; - for (j = 0, len = relTags.length; j < len; j++) { - tag = relTags[j]; - elements = document.getElementsByTagName(tag); - for (k = 0, len1 = elements.length; k < len1; k++) { - element = elements[k]; - if (element.hasAttribute("rel") && element.rel.toLowerCase() === value) { - this.followLink(element); - return true; - } - } - } + followLink: function(linkElement) { + if (linkElement.nodeName.toLowerCase() === "link") { + return (window.location.href = linkElement.href); + } else { + linkElement.scrollIntoView(); + return DomUtils.simulateClick(linkElement); + } + }, + findAndFollowLink: function(linkStrings) { + var boundingClientRect, + candidateLink, + candidateLinks, + computedStyle, + exactWordRegex, + i, + j, + k, + l, + len, + len1, + len2, + len3, + link, + linkMatches, + linkString, + links, + linksXPath, + m, + n, + ref, + ref1; + linksXPath = DomUtils.makeXPath([ + "a", + "*[@onclick or @role='link' or contains(@class, 'button')]" + ]); + links = DomUtils.evaluateXPath( + linksXPath, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + ); + candidateLinks = []; + for (i = j = ref = links.snapshotLength - 1; j >= 0; i = j += -1) { + link = links.snapshotItem(i); + boundingClientRect = link.getBoundingClientRect(); + if (boundingClientRect.width === 0 || boundingClientRect.height === 0) { + continue; + } + computedStyle = window.getComputedStyle(link, null); + if ( + computedStyle.getPropertyValue("visibility") !== "visible" || + computedStyle.getPropertyValue("display") === "none" + ) { + continue; + } + linkMatches = false; + for (k = 0, len = linkStrings.length; k < len; k++) { + linkString = linkStrings[k]; + if ( + link.innerText.toLowerCase().indexOf(linkString) !== -1 || + 0 <= + ((ref1 = link.value) != null + ? typeof ref1.indexOf === "function" + ? ref1.indexOf(linkString) + : void 0 + : void 0) + ) { + linkMatches = true; + break; + } + } + if (!linkMatches) { + continue; + } + candidateLinks.push(link); + } + if (candidateLinks.length === 0) { + return; + } + for (l = 0, len1 = candidateLinks.length; l < len1; l++) { + link = candidateLinks[l]; + link.wordCount = link.innerText.trim().split(/\s+/).length; + } + candidateLinks.forEach(function(a, i) { + return (a.originalIndex = i); + }); + candidateLinks = candidateLinks + .sort(function(a, b) { + if (a.wordCount === b.wordCount) { + return a.originalIndex - b.originalIndex; + } else { + return a.wordCount - b.wordCount; + } + }) + .filter(function(a) { + return a.wordCount <= candidateLinks[0].wordCount + 1; + }); + for (m = 0, len2 = linkStrings.length; m < len2; m++) { + linkString = linkStrings[m]; + exactWordRegex = + /\b/.test(linkString[0]) || /\b/.test(linkString[linkString.length - 1]) + ? new RegExp("\\b" + linkString + "\\b", "i") + : new RegExp(linkString, "i"); + for (n = 0, len3 = candidateLinks.length; n < len3; n++) { + candidateLink = candidateLinks[n]; + if ( + exactWordRegex.test(candidateLink.innerText) || + (candidateLink.value && exactWordRegex.test(candidateLink.value)) + ) { + this.followLink(candidateLink); + return true; + } + } + } + return false; + }, + findAndFollowRel: function(value) { + var element, elements, j, k, len, len1, relTags, tag; + relTags = ["link", "a", "area"]; + for (j = 0, len = relTags.length; j < len; j++) { + tag = relTags[j]; + elements = document.getElementsByTagName(tag); + for (k = 0, len1 = elements.length; k < len1; k++) { + element = elements[k]; + if ( + element.hasAttribute("rel") && + element.rel.toLowerCase() === value + ) { + this.followLink(element); + return true; + } + } + } + }, + textInputXPath: function() { + var inputElements, textInputTypes; + textInputTypes = [ + "text", + "search", + "email", + "url", + "number", + "password", + "date", + "tel" + ]; + inputElements = [ + "input[" + + "(" + + textInputTypes + .map(function(type) { + return '@type="' + type + '"'; + }) + .join(" or ") + + "or not(@type))" + + " and not(@disabled or @readonly)]", + "textarea", + "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" + ]; + return typeof DomUtils !== "undefined" && DomUtils !== null + ? DomUtils.makeXPath(inputElements) + : void 0; }, - textInputXPath : function() { - var inputElements, textInputTypes; - textInputTypes = ["text", "search", "email", "url", "number", "password", "date", "tel"]; - inputElements = [ - "input[" + "(" + textInputTypes.map(function(type) { - return '@type="' + type + '"'; - }).join(" or ") + "or not(@type))" + " and not(@disabled or @readonly)]", "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" - ]; - return typeof DomUtils !== "undefined" && DomUtils !== null ? DomUtils.makeXPath(inputElements) : void 0; - }, focusInput: function(count) { - var element, elements, hint, hints, i, recentlyFocusedElement, resultSet, selectedInputIndex, tuple, visibleInputs; - resultSet = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + var element, + elements, + hint, + hints, + i, + recentlyFocusedElement, + resultSet, + selectedInputIndex, + tuple, + visibleInputs; + resultSet = DomUtils.evaluateXPath( + textInputXPath, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + ); visibleInputs = (function() { var j, ref, results; results = []; @@ -734,8 +1151,8 @@ var VimiumNormal = { })(); visibleInputs.sort(function(arg, arg1) { var element1, element2, i1, i2, tabDifference; - element1 = arg.element, i1 = arg.index; - element2 = arg1.element, i2 = arg1.index; + (element1 = arg.element), (i1 = arg.index); + (element2 = arg1.element), (i2 = arg1.index); if (element1.tabIndex > 0) { if (element2.tabIndex > 0) { tabDifference = element1.tabIndex - element2.tabIndex; @@ -758,9 +1175,13 @@ var VimiumNormal = { return; } recentlyFocusedElement = lastFocusedInput(); - selectedInputIndex = count === 1 ? (elements = visibleInputs.map(function(visibleInput) { - return visibleInput.element; - }), Math.max(0, elements.indexOf(recentlyFocusedElement))) : Math.min(count, visibleInputs.length) - 1; + selectedInputIndex = + count === 1 + ? ((elements = visibleInputs.map(function(visibleInput) { + return visibleInput.element; + })), + Math.max(0, elements.indexOf(recentlyFocusedElement))) + : Math.min(count, visibleInputs.length) - 1; hints = (function() { var j, len, results; results = []; @@ -768,8 +1189,8 @@ var VimiumNormal = { tuple = visibleInputs[j]; hint = DomUtils.createElement("div"); hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"; - hint.style.left = (tuple.rect.left - 1) + window.scrollX + "px"; - hint.style.top = (tuple.rect.top - 1) + window.scrollY + "px"; + hint.style.left = tuple.rect.left - 1 + window.scrollX + "px"; + hint.style.top = tuple.rect.top - 1 + window.scrollY + "px"; hint.style.width = tuple.rect.width + "px"; hint.style.height = tuple.rect.height + "px"; results.push(hint); @@ -778,8 +1199,7 @@ var VimiumNormal = { })(); return new FocusSelector(hints, visibleInputs, selectedInputIndex); } - }; - +}; export function MiscVimium() { if (window.forTrusted == null) { @@ -797,29 +1217,33 @@ export function MiscVimium() { var windowHasFocus; windowHasFocus = null; DomUtils.documentReady(function() { - return windowHasFocus = document.hasFocus(); + return (windowHasFocus = document.hasFocus()); }); - window.addEventListener("focus", forTrusted(function(event) { - if (event.target === window) { - windowHasFocus = true; - } - return true; - })); - window.addEventListener("blur", forTrusted(function(event) { - if (event.target === window) { - windowHasFocus = false; - } - return true; - })); + window.addEventListener( + "focus", + forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = true; + } + return true; + }) + ); + window.addEventListener( + "blur", + forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = false; + } + return true; + }) + ); return function() { return windowHasFocus; }; }; -}; - -export { Rect } -export { DomUtils } -export { LocalHints } -export { VimiumNormal } - +} +export { Rect }; +export { DomUtils }; +export { LocalHints }; +export { VimiumNormal }; From ae1df350a357bb84b453416e40c2d022071b2009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 11:38:26 -0400 Subject: [PATCH 17/77] Fixed typo --- interfacer/test/tty/tty_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index f31f2445..8c620c40 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -191,7 +191,7 @@ var _ = Describe("Showing a basic webpage", func() { }) Describe("Text positioning", func() { - It("should position the left/right-aligned coloumns", func() { + It("should position the left/right-aligned columns", func() { Expect("Smörgåsbord▄(Swedish:").To(BeInFrameAt(12, 10)) Expect("The▄Swedish▄word").To(BeInFrameAt(42, 10)) }) From 034d9c4a0af1a63d6b24a2e68e3d9f90c7605db2 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 6 Nov 2018 17:11:52 +0900 Subject: [PATCH 18/77] Update Go `dep` to v0.5.0 --- interfacer/Gopkg.lock | 182 ++++++++++++++++++++++++++++++-- interfacer/contrib/setup_dep.sh | 2 +- 2 files changed, 172 insertions(+), 12 deletions(-) diff --git a/interfacer/Gopkg.lock b/interfacer/Gopkg.lock index b92c9810..3dfeb61c 100644 --- a/interfacer/Gopkg.lock +++ b/interfacer/Gopkg.lock @@ -2,182 +2,342 @@ [[projects]] + digest = "1:b0fe84bcee1d0c3579d855029ccd3a76deea187412da2976985e4946289dbb2c" name = "github.com/NYTimes/gziphandler" packages = ["."] + pruneopts = "" revision = "2600fb119af974220d3916a5916d6e31176aac1b" version = "v1.0.1" [[projects]] + digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" name = "github.com/fsnotify/fsnotify" packages = ["."] + pruneopts = "" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" version = "v1.4.7" [[projects]] branch = "master" + digest = "1:4416ad04caf950d004921268f777e418f8c328080ccf6a91e799f977dc128808" name = "github.com/gdamore/encoding" packages = ["."] + pruneopts = "" revision = "b23993cbb6353f0e6aa98d0ee318a34728f628b9" [[projects]] branch = "master" + digest = "1:0736ea5f02b4e3abf1d97613105a012be448ff80ba8467120a03172788f6fda0" name = "github.com/gdamore/tcell" - packages = [".","terminfo"] + packages = [ + ".", + "terminfo", + ] + pruneopts = "" revision = "b3cebc399d6f98536af845ed8a5144ab586f6759" [[projects]] + digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a" name = "github.com/go-errors/errors" packages = ["."] + pruneopts = "" revision = "a6af135bd4e28680facf08a3d206b454abc877a4" version = "v1.0.1" [[projects]] + digest = "1:64d212c703a2b94054be0ce470303286b177ad260b2f89a307e3d1bb6c073ef6" name = "github.com/gorilla/websocket" packages = ["."] + pruneopts = "" revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:9b7c5846d70f425d7fe279595e32a20994c6075e87be03b5c367ed07280877c5" name = "github.com/hashicorp/hcl" - packages = [".","hcl/ast","hcl/parser","hcl/printer","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"] + packages = [ + ".", + "hcl/ast", + "hcl/parser", + "hcl/printer", + "hcl/scanner", + "hcl/strconv", + "hcl/token", + "json/parser", + "json/scanner", + "json/token", + ] + pruneopts = "" revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" [[projects]] + digest = "1:863ba095ae0954100963826333bd1503c169424bb798d9ce800d9f6651f4c234" name = "github.com/lucasb-eyer/go-colorful" packages = ["."] + pruneopts = "" revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82" version = "v1.0" [[projects]] + digest = "1:961dc3b1d11f969370533390fdf203813162980c858e1dabe827b60940c909a5" name = "github.com/magiconair/properties" packages = ["."] + pruneopts = "" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" [[projects]] + digest = "1:81e673df85e765593a863f67cba4544cf40e8919590f04d67664940786c2b61a" name = "github.com/mattn/go-runewidth" packages = ["."] + pruneopts = "" revision = "9e777a8366cce605130a531d2cd6363d07ad7317" version = "v0.0.2" [[projects]] branch = "master" + digest = "1:f43ed2c836208c14f45158fd01577c985688a4d11cf9fd475a939819fef3b321" name = "github.com/mitchellh/mapstructure" packages = ["."] + pruneopts = "" revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" [[projects]] + digest = "1:1eece6e2aa8a8f94e0461483229f0c4b0555c17e76e0e7e6b8597c2093d269f8" name = "github.com/onsi/ginkgo" - packages = [".","config","internal/codelocation","internal/containernode","internal/failer","internal/leafnodes","internal/remote","internal/spec","internal/spec_iterator","internal/specrunner","internal/suite","internal/testingtproxy","internal/writer","reporters","reporters/stenographer","reporters/stenographer/support/go-colorable","reporters/stenographer/support/go-isatty","types"] + packages = [ + ".", + "config", + "internal/codelocation", + "internal/containernode", + "internal/failer", + "internal/leafnodes", + "internal/remote", + "internal/spec", + "internal/spec_iterator", + "internal/specrunner", + "internal/suite", + "internal/testingtproxy", + "internal/writer", + "reporters", + "reporters/stenographer", + "reporters/stenographer/support/go-colorable", + "reporters/stenographer/support/go-isatty", + "types", + ] + pruneopts = "" revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf" version = "v1.5.0" [[projects]] + digest = "1:ae1d23cbf8c1dfdc3ebffa41f28d31fc7110323596e9247e382c9cc7df4243a9" name = "github.com/onsi/gomega" - packages = [".","format","internal/assertion","internal/asyncassertion","internal/oraclematcher","internal/testingtsupport","matchers","matchers/support/goraph/bipartitegraph","matchers/support/goraph/edge","matchers/support/goraph/node","matchers/support/goraph/util","types"] + packages = [ + ".", + "format", + "internal/assertion", + "internal/asyncassertion", + "internal/oraclematcher", + "internal/testingtsupport", + "matchers", + "matchers/support/goraph/bipartitegraph", + "matchers/support/goraph/edge", + "matchers/support/goraph/node", + "matchers/support/goraph/util", + "types", + ] + pruneopts = "" revision = "62bff4df71bdbc266561a0caee19f0594b17c240" version = "v1.4.0" [[projects]] + digest = "1:894aef961c056b6d85d12bac890bf60c44e99b46292888bfa66caf529f804457" name = "github.com/pelletier/go-toml" packages = ["."] + pruneopts = "" revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" version = "v1.2.0" [[projects]] + digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" name = "github.com/pkg/errors" packages = ["."] + pruneopts = "" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" [[projects]] + digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] branch = "master" + digest = "1:adea3123626b97cc02423f77f21d6cd85c9461c3ad3221d62af604660c17a5a6" name = "github.com/shibukawa/configdir" packages = ["."] + pruneopts = "" revision = "e180dbdc8da04c4fa04272e875ce64949f38bd3e" [[projects]] + digest = "1:7ba2551c9a8de293bc575dbe2c0d862c52252d26f267f784547f059f512471c8" name = "github.com/spf13/afero" - packages = [".","mem"] + packages = [ + ".", + "mem", + ] + pruneopts = "" revision = "787d034dfe70e44075ccc060d346146ef53270ad" version = "v1.1.1" [[projects]] + digest = "1:d0b38ba6da419a6d4380700218eeec8623841d44a856bb57369c172fbf692ab4" name = "github.com/spf13/cast" packages = ["."] + pruneopts = "" revision = "8965335b8c7107321228e3e3702cab9832751bac" version = "v1.2.0" [[projects]] branch = "master" + digest = "1:104517520aab91164020ab6524a5d6b7cafc641b2e42ac6236f6ac1deac4f66a" name = "github.com/spf13/jwalterweatherman" packages = ["."] + pruneopts = "" revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" [[projects]] + digest = "1:8e243c568f36b09031ec18dff5f7d2769dcf5ca4d624ea511c8e3197dc3d352d" name = "github.com/spf13/pflag" packages = ["."] + pruneopts = "" revision = "583c0c0531f06d5278b7d917446061adc344b5cd" version = "v1.0.1" [[projects]] + digest = "1:3dab237cd3263a290d771d133fed777bb56c22e380b00ebe92e6531d5c8d3d0c" name = "github.com/spf13/viper" packages = ["."] + pruneopts = "" revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736" version = "v1.0.2" [[projects]] + digest = "1:c587772fb8ad29ad4db67575dad25ba17a51f072ff18a22b4f0257a4d9c24f75" name = "github.com/stretchr/testify" - packages = ["assert","require"] + packages = [ + "assert", + "require", + ] + pruneopts = "" revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" version = "v1.2.2" [[projects]] + digest = "1:c1adfb0791d174b2e69276e8694bc89949ceb108a114887457750ec8611ad793" name = "github.com/ulule/limiter" - packages = [".","drivers/middleware/stdlib","drivers/store/common","drivers/store/memory"] + packages = [ + ".", + "drivers/middleware/stdlib", + "drivers/store/common", + "drivers/store/memory", + ] + pruneopts = "" revision = "f0ff82677fc4826ddae9ac45f2e11462fa245dad" version = "v2.2.0" [[projects]] branch = "master" + digest = "1:c9bd77463c992cdec2514ada56f8cd78d9013aa6933b43f16d9c97fd0931659e" name = "golang.org/x/net" - packages = ["html","html/atom","html/charset"] + packages = [ + "html", + "html/atom", + "html/charset", + ] + pruneopts = "" revision = "1e491301e022f8f977054da4c2d852decd59571f" [[projects]] branch = "master" + digest = "1:d43e30c702e549b855381ec0d4349fbd2089063754d413a057abf5e2cc6e9b96" name = "golang.org/x/sys" - packages = ["unix","windows","windows/registry"] + packages = [ + "unix", + "windows", + "windows/registry", + ] + pruneopts = "" revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb" [[projects]] + digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" name = "golang.org/x/text" - packages = ["encoding","encoding/charmap","encoding/htmlindex","encoding/internal","encoding/internal/identifier","encoding/japanese","encoding/korean","encoding/simplifiedchinese","encoding/traditionalchinese","encoding/unicode","internal/gen","internal/tag","internal/triegen","internal/ucd","internal/utf8internal","language","runes","transform","unicode/cldr","unicode/norm"] + packages = [ + "encoding", + "encoding/charmap", + "encoding/htmlindex", + "encoding/internal", + "encoding/internal/identifier", + "encoding/japanese", + "encoding/korean", + "encoding/simplifiedchinese", + "encoding/traditionalchinese", + "encoding/unicode", + "internal/gen", + "internal/tag", + "internal/triegen", + "internal/ucd", + "internal/utf8internal", + "language", + "runes", + "transform", + "unicode/cldr", + "unicode/norm", + ] + pruneopts = "" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] + digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a758e47b231056184a2433d9870401e6d2a15fc69c7f9b35780bd57fbc0fc24e" + input-imports = [ + "github.com/NYTimes/gziphandler", + "github.com/gdamore/tcell", + "github.com/gdamore/tcell/terminfo", + "github.com/go-errors/errors", + "github.com/gorilla/websocket", + "github.com/onsi/ginkgo", + "github.com/onsi/gomega", + "github.com/onsi/gomega/types", + "github.com/shibukawa/configdir", + "github.com/spf13/pflag", + "github.com/spf13/viper", + "github.com/ulule/limiter", + "github.com/ulule/limiter/drivers/middleware/stdlib", + "github.com/ulule/limiter/drivers/store/memory", + "golang.org/x/sys/windows/registry", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/interfacer/contrib/setup_dep.sh b/interfacer/contrib/setup_dep.sh index 685c4a57..568f6394 100755 --- a/interfacer/contrib/setup_dep.sh +++ b/interfacer/contrib/setup_dep.sh @@ -2,7 +2,7 @@ set -e # Install `dep` the current defacto dependency for Golang -GOLANG_DEP_VERSION=0.3.2 +GOLANG_DEP_VERSION=0.5.0 dep_url=https://github.com/golang/dep/releases/download/v$GOLANG_DEP_VERSION/dep-linux-amd64 if [ -z $GOPATH ]; then From 11f746bc085a3cd86233979fb63dabdaf8bddc56 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 6 Nov 2018 18:04:16 +0900 Subject: [PATCH 19/77] Newer NVM formats package.lock differently --- webext/package-lock.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/webext/package-lock.json b/webext/package-lock.json index bf5a808f..b2a8da0f 100644 --- a/webext/package-lock.json +++ b/webext/package-lock.json @@ -4624,7 +4624,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5039,7 +5040,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5095,6 +5097,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "2.1.1" } @@ -5138,12 +5141,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, From d17cb59be4e9a6944fdf1b10cb8b250babd1b59c Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 6 Nov 2018 18:06:07 +0900 Subject: [PATCH 20/77] Update FF Marionette commands In Firefox 63 an old syntax for Marionette commands was deprecated. Updating mostly just meant prepending `WebDriver` to existing commands. This should fix most problems in #232 --- .travis.yml | 2 +- README.md | 2 +- interfacer/src/browsh/firefox.go | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 994f15f9..4cd7bad9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ go: addons: # Use the full version number with ".0" if needed. This value is scraped by setup scripts. - firefox: "60.0" + firefox: "63.0" apt: packages: - rpm diff --git a/README.md b/README.md index 25eacc12..b3a2f242 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ is running somewhere else on mains electricity. ## Installation Download a binary from the [releases](https://github.com/browsh-org/browsh/releases) (~7MB). -You will need to have [Firefox 57](https://www.mozilla.org/en-US/firefox/new/) (or higher) aleady installed. +You will need to have [Firefox 63](https://www.mozilla.org/en-US/firefox/new/) (or higher) aleady installed. Or download and run the Docker image (~230MB) with: `docker run --rm -it browsh/browsh` diff --git a/interfacer/src/browsh/firefox.go b/interfacer/src/browsh/firefox.go index da6d6c1e..3154c35f 100644 --- a/interfacer/src/browsh/firefox.go +++ b/interfacer/src/browsh/firefox.go @@ -214,7 +214,7 @@ func firefoxMarionette() { } marionette = conn readMarionette() - sendFirefoxCommand("newSession", map[string]interface{}{}) + sendFirefoxCommand("WebDriver:NewSession", map[string]interface{}{}) } // Install the Browsh extension that was bundled with `go-bindata` under @@ -228,20 +228,20 @@ func installWebextension() { defer os.Remove(file.Name()) ioutil.WriteFile(file.Name(), []byte(data), 0644) args := map[string]interface{}{"path": file.Name()} - sendFirefoxCommand("addon:install", args) + sendFirefoxCommand("Addon:Install", args) } // Set a Firefox preference as you would in `about:config` // `value` needs to be supplied with quotes if it's to be used as a JS string func setFFPreference(key string, value string) { - sendFirefoxCommand("setContext", map[string]interface{}{"value": "chrome"}) + sendFirefoxCommand("Marionette:SetContext", map[string]interface{}{"value": "chrome"}) script := fmt.Sprintf(` Components.utils.import("resource://gre/modules/Preferences.jsm"); prefs = new Preferences({defaultBranch: false}); prefs.set("%s", %s);`, key, value) args := map[string]interface{}{"script": script} - sendFirefoxCommand("executeScript", args) - sendFirefoxCommand("setContext", map[string]interface{}{"value": "content"}) + sendFirefoxCommand("WebDriver:ExecuteScript", args) + sendFirefoxCommand("Marionette:SetContext", map[string]interface{}{"value": "content"}) } // Consume output from Marionette, we don't do anything with it. It"s just @@ -309,5 +309,5 @@ func startFirefox() { } func quitFirefox() { - sendFirefoxCommand("quitApplication", map[string]interface{}{}) + sendFirefoxCommand("Marionette:Quit", map[string]interface{}{}) } From bf44f91a2762f3dd86718c2cacb51c77c3f263e0 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 6 Nov 2018 18:42:35 +0900 Subject: [PATCH 21/77] Update JS and Go deps. Bump Browsh to v1.5.0 --- interfacer/Gopkg.lock | 216 +- interfacer/Gopkg.toml | 69 +- interfacer/src/browsh/version.go | 2 +- webext/package-lock.json | 4165 +++++++++++++++--------------- webext/package.json | 18 +- 5 files changed, 2231 insertions(+), 2239 deletions(-) diff --git a/interfacer/Gopkg.lock b/interfacer/Gopkg.lock index 3dfeb61c..1e356b04 100644 --- a/interfacer/Gopkg.lock +++ b/interfacer/Gopkg.lock @@ -2,67 +2,58 @@ [[projects]] - digest = "1:b0fe84bcee1d0c3579d855029ccd3a76deea187412da2976985e4946289dbb2c" + digest = "1:0803645e1f57fb5271a6edc7570b9ea59bac2e5de67957075a43f3d74c8dbd97" name = "github.com/NYTimes/gziphandler" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "2600fb119af974220d3916a5916d6e31176aac1b" version = "v1.0.1" [[projects]] - digest = "1:56c130d885a4aacae1dd9c7b71cfe39912c7ebc1ff7d2b46083c8812996dc43b" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "" - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - digest = "1:eb53021a8aa3f599d29c7102e65026242bdedce998a54837dc67f14b6a97c5fd" + digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" name = "github.com/fsnotify/fsnotify" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" version = "v1.4.7" [[projects]] branch = "master" - digest = "1:4416ad04caf950d004921268f777e418f8c328080ccf6a91e799f977dc128808" + digest = "1:40d0056c1b1f503c366ba441df92a82b5a2654d6f3747b1689a611eb5c9ce0a2" name = "github.com/gdamore/encoding" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "b23993cbb6353f0e6aa98d0ee318a34728f628b9" [[projects]] - branch = "master" - digest = "1:0736ea5f02b4e3abf1d97613105a012be448ff80ba8467120a03172788f6fda0" + digest = "1:00b9cce210566117aff926677c005aeaea6c85374e67bdcb72783af237c48f97" name = "github.com/gdamore/tcell" packages = [ ".", "terminfo", ] - pruneopts = "" - revision = "b3cebc399d6f98536af845ed8a5144ab586f6759" + pruneopts = "UT" + revision = "de7e78efa4a71b3f36c7154989c529dbdf9ae623" + version = "v1.1.0" [[projects]] - digest = "1:968d8903d598e3fae738325d3410f33f07ea6a2b9ee5591e9c262ee37df6845a" + digest = "1:aacef5f5e45685f2aeda5534d0a750dee6859de7e9088cdd06192787bb01ae6d" name = "github.com/go-errors/errors" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "a6af135bd4e28680facf08a3d206b454abc877a4" version = "v1.0.1" [[projects]] - digest = "1:64d212c703a2b94054be0ce470303286b177ad260b2f89a307e3d1bb6c073ef6" + digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d" name = "github.com/gorilla/websocket" packages = ["."] - pruneopts = "" - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" + pruneopts = "UT" + revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d" + version = "v1.4.0" [[projects]] - branch = "master" - digest = "1:9b7c5846d70f425d7fe279595e32a20994c6075e87be03b5c367ed07280877c5" + digest = "1:c0d19ab64b32ce9fe5cf4ddceba78d5bc9807f0016db6b1183599da3dcc24d10" name = "github.com/hashicorp/hcl" packages = [ ".", @@ -76,43 +67,58 @@ "json/scanner", "json/token", ] - pruneopts = "" - revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168" + pruneopts = "UT" + revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" + version = "v1.0.0" [[projects]] - digest = "1:863ba095ae0954100963826333bd1503c169424bb798d9ce800d9f6651f4c234" + branch = "master" + digest = "1:59392ed8afb901aab4287d4894df8191722e34f3957716f4350c8c133ce99046" + name = "github.com/hpcloud/tail" + packages = [ + ".", + "ratelimiter", + "util", + "watch", + "winfile", + ] + pruneopts = "UT" + revision = "a1dbeea552b7c8df4b542c66073e393de198a800" + +[[projects]] + digest = "1:c65a16ac77d0b1aefc7009cabb6ac5ad05def02025f5be85f450c03f52cc6f86" name = "github.com/lucasb-eyer/go-colorful" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "345fbb3dbcdb252d9985ee899a84963c0fa24c82" version = "v1.0" [[projects]] - digest = "1:961dc3b1d11f969370533390fdf203813162980c858e1dabe827b60940c909a5" + digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" name = "github.com/magiconair/properties" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" [[projects]] - digest = "1:81e673df85e765593a863f67cba4544cf40e8919590f04d67664940786c2b61a" + digest = "1:cdb899c199f907ac9fb50495ec71212c95cb5b0e0a8ee0800da0238036091033" name = "github.com/mattn/go-runewidth" packages = ["."] - pruneopts = "" - revision = "9e777a8366cce605130a531d2cd6363d07ad7317" - version = "v0.0.2" + pruneopts = "UT" + revision = "ce7b0b5c7b45a81508558cd1dba6bb1e4ddb51bb" + version = "v0.0.3" [[projects]] - branch = "master" - digest = "1:f43ed2c836208c14f45158fd01577c985688a4d11cf9fd475a939819fef3b321" + digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" name = "github.com/mitchellh/mapstructure" packages = ["."] - pruneopts = "" - revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac" + pruneopts = "UT" + revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" + version = "v1.1.2" [[projects]] - digest = "1:1eece6e2aa8a8f94e0461483229f0c4b0555c17e76e0e7e6b8597c2093d269f8" + digest = "1:42e29deef12327a69123b9cb2cb45fee4af5c12c2a23c6e477338279a052703f" name = "github.com/onsi/ginkgo" packages = [ ".", @@ -134,12 +140,12 @@ "reporters/stenographer/support/go-isatty", "types", ] - pruneopts = "" - revision = "fa5fabab2a1bfbd924faf4c067d07ae414e2aedf" - version = "v1.5.0" + pruneopts = "UT" + revision = "3774a09d95489ccaa16032e0770d08ea77ba6184" + version = "v1.6.0" [[projects]] - digest = "1:ae1d23cbf8c1dfdc3ebffa41f28d31fc7110323596e9247e382c9cc7df4243a9" + digest = "1:ab54eea8d482272009e9e4af07d4d9b5236c27b4d8c54a3f2c99d163be883eca" name = "github.com/onsi/gomega" packages = [ ".", @@ -155,98 +161,79 @@ "matchers/support/goraph/util", "types", ] - pruneopts = "" - revision = "62bff4df71bdbc266561a0caee19f0594b17c240" - version = "v1.4.0" + pruneopts = "UT" + revision = "7615b9433f86a8bdf29709bf288bc4fd0636a369" + version = "v1.4.2" [[projects]] - digest = "1:894aef961c056b6d85d12bac890bf60c44e99b46292888bfa66caf529f804457" + digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" name = "github.com/pelletier/go-toml" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" version = "v1.2.0" [[projects]] - digest = "1:7365acd48986e205ccb8652cc746f09c8b7876030d53710ea6ef7d0bd0dcd7ca" + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "645ef00459ed84a119197bfb8d8205042c6df63d" version = "v0.8.0" -[[projects]] - digest = "1:256484dbbcd271f9ecebc6795b2df8cad4c458dd0f5fd82a8c2fa0c29f233411" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - [[projects]] branch = "master" - digest = "1:adea3123626b97cc02423f77f21d6cd85c9461c3ad3221d62af604660c17a5a6" + digest = "1:8209ed8bf2336848aa760c11e809f93ba96fffa64d3c2948e970a362a46e534b" name = "github.com/shibukawa/configdir" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "e180dbdc8da04c4fa04272e875ce64949f38bd3e" [[projects]] - digest = "1:7ba2551c9a8de293bc575dbe2c0d862c52252d26f267f784547f059f512471c8" + digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd" name = "github.com/spf13/afero" packages = [ ".", "mem", ] - pruneopts = "" - revision = "787d034dfe70e44075ccc060d346146ef53270ad" - version = "v1.1.1" + pruneopts = "UT" + revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" + version = "v1.1.2" [[projects]] - digest = "1:d0b38ba6da419a6d4380700218eeec8623841d44a856bb57369c172fbf692ab4" + digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" name = "github.com/spf13/cast" packages = ["."] - pruneopts = "" - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" + pruneopts = "UT" + revision = "8c9545af88b134710ab1cd196795e7f2388358d7" + version = "v1.3.0" [[projects]] - branch = "master" - digest = "1:104517520aab91164020ab6524a5d6b7cafc641b2e42ac6236f6ac1deac4f66a" + digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" name = "github.com/spf13/jwalterweatherman" packages = ["."] - pruneopts = "" - revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" + pruneopts = "UT" + revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" + version = "v1.0.0" [[projects]] - digest = "1:8e243c568f36b09031ec18dff5f7d2769dcf5ca4d624ea511c8e3197dc3d352d" + digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" name = "github.com/spf13/pflag" packages = ["."] - pruneopts = "" - revision = "583c0c0531f06d5278b7d917446061adc344b5cd" - version = "v1.0.1" + pruneopts = "UT" + revision = "298182f68c66c05229eb03ac171abe6e309ee79a" + version = "v1.0.3" [[projects]] - digest = "1:3dab237cd3263a290d771d133fed777bb56c22e380b00ebe92e6531d5c8d3d0c" + digest = "1:214775c11fd26da94a100111a62daa25339198a4f9c57cb4aab352da889f5b93" name = "github.com/spf13/viper" packages = ["."] - pruneopts = "" - revision = "b5e8006cbee93ec955a89ab31e0e3ce3204f3736" - version = "v1.0.2" - -[[projects]] - digest = "1:c587772fb8ad29ad4db67575dad25ba17a51f072ff18a22b4f0257a4d9c24f75" - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require", - ] - pruneopts = "" - revision = "f35b8ab0b5a2cef36673838d662e249dd9c94686" - version = "v1.2.2" + pruneopts = "UT" + revision = "2c12c60302a5a0e62ee102ca9bc996277c2f64f5" + version = "v1.2.1" [[projects]] - digest = "1:c1adfb0791d174b2e69276e8694bc89949ceb108a114887457750ec8611ad793" + digest = "1:46b32b8bb81eff9edd36937c2f7e486221fd3f93f17045385971f966e697f971" name = "github.com/ulule/limiter" packages = [ ".", @@ -254,36 +241,36 @@ "drivers/store/common", "drivers/store/memory", ] - pruneopts = "" - revision = "f0ff82677fc4826ddae9ac45f2e11462fa245dad" - version = "v2.2.0" + pruneopts = "UT" + revision = "cfd0e758da796489f1274da631a71ab637009c2f" + version = "v2.2.1" [[projects]] branch = "master" - digest = "1:c9bd77463c992cdec2514ada56f8cd78d9013aa6933b43f16d9c97fd0931659e" + digest = "1:5193d913046443e59093d66a97a40c51f4a5ea4ceba60f3b3ecf89694de5d16f" name = "golang.org/x/net" packages = [ "html", "html/atom", "html/charset", ] - pruneopts = "" - revision = "1e491301e022f8f977054da4c2d852decd59571f" + pruneopts = "UT" + revision = "10aee181995363b41f712a55844a0dd52ea04646" [[projects]] branch = "master" - digest = "1:d43e30c702e549b855381ec0d4349fbd2089063754d413a057abf5e2cc6e9b96" + digest = "1:98f84f98eb433d0c9e5d3475a6c4fceaa619c989f04880cb683d34012f76dab8" name = "golang.org/x/sys" packages = [ "unix", "windows", "windows/registry", ] - pruneopts = "" - revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb" + pruneopts = "UT" + revision = "7155702f2d47d94b134229da97195d0130cab001" [[projects]] - digest = "1:5acd3512b047305d49e8763eef7ba423901e85d5dd2fd1e71778a0ea8de10bd4" + digest = "1:4392fcf42d5cf0e3ff78c96b2acf8223d49e4fdc53eb77c99d2f8dfe4680e006" name = "golang.org/x/text" packages = [ "encoding", @@ -307,15 +294,30 @@ "unicode/cldr", "unicode/norm", ] - pruneopts = "" + pruneopts = "UT" revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" version = "v0.3.0" [[projects]] - digest = "1:f0620375dd1f6251d9973b5f2596228cc8042e887cd7f827e4220bc1ce8c30e2" + digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" + name = "gopkg.in/fsnotify/fsnotify.v1" + packages = ["."] + pruneopts = "UT" + revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" + version = "v1.4.7" + +[[projects]] + digest = "1:3c839a777de0e6da035c9de900b60cbec463b0a89351192c1ea083eaf9e0fce0" + name = "gopkg.in/tomb.v1" + packages = ["."] + pruneopts = "UT" + revision = "c131134a1947e9afd9cecfe11f4c6dff0732ae58" + +[[projects]] + digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" name = "gopkg.in/yaml.v2" packages = ["."] - pruneopts = "" + pruneopts = "UT" revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" version = "v2.2.1" diff --git a/interfacer/Gopkg.toml b/interfacer/Gopkg.toml index 3f709684..769be61c 100644 --- a/interfacer/Gopkg.toml +++ b/interfacer/Gopkg.toml @@ -1,19 +1,74 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + [[constraint]] - name = "github.com/gorilla/websocket" - version = "1.2.0" + name = "github.com/NYTimes/gziphandler" + version = "1.0.1" [[constraint]] - branch = "master" name = "github.com/gdamore/tcell" + version = "1.1.0" [[constraint]] - name = "github.com/shuLhan/go-bindata" - version = "3.2.0" + name = "github.com/go-errors/errors" + version = "1.0.1" + +[[constraint]] + name = "github.com/gorilla/websocket" + version = "1.4.0" + +[[constraint]] + name = "github.com/onsi/ginkgo" + version = "1.6.0" + +[[constraint]] + name = "github.com/onsi/gomega" + version = "1.4.2" [[constraint]] branch = "master" name = "github.com/shibukawa/configdir" [[constraint]] - name = "github.com/NYTimes/gziphandler" - version = "v1.0.1" + name = "github.com/spf13/pflag" + version = "1.0.3" + +[[constraint]] + name = "github.com/spf13/viper" + version = "1.2.1" + +[[constraint]] + name = "github.com/ulule/limiter" + version = "2.2.1" + +[[constraint]] + branch = "master" + name = "golang.org/x/sys" + +[prune] + go-tests = true + unused-packages = true diff --git a/interfacer/src/browsh/version.go b/interfacer/src/browsh/version.go index 53d6caae..fffc26f2 100644 --- a/interfacer/src/browsh/version.go +++ b/interfacer/src/browsh/version.go @@ -1,3 +1,3 @@ package browsh -var browshVersion = "1.4.13" +var browshVersion = "1.5.0" diff --git a/webext/package-lock.json b/webext/package-lock.json index b2a8da0f..ca9e84d3 100644 --- a/webext/package-lock.json +++ b/webext/package-lock.json @@ -4,7 +4,7 @@ "dependencies": { "@babel/code-frame": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.44.tgz", "integrity": "sha512-cuAuTTIQ9RqcFRJ/Y8PvTh+paepNcaGxwQwjIDRWPXmzzyAeCO4KqS9ikMvq0MCbRk6GlYKwfzStrcP3/jSL8g==", "dev": true, "requires": { @@ -13,7 +13,7 @@ }, "@babel/generator": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz", "integrity": "sha512-5xVb7hlhjGcdkKpMXgicAVgx8syK5VJz193k0i/0sLP6DzE6lRrU1K3B/rFefgdo9LPGMAOOOAWW4jycj07ShQ==", "dev": true, "requires": { @@ -34,7 +34,7 @@ }, "@babel/helper-function-name": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz", "integrity": "sha512-MHRG2qZMKMFaBavX0LWpfZ2e+hLloT++N7rfM3DYOMUOGCD8cVjqZpwiL8a0bOX3IYcQev1ruciT0gdFFRTxzg==", "dev": true, "requires": { @@ -45,7 +45,7 @@ }, "@babel/helper-get-function-arity": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz", "integrity": "sha512-w0YjWVwrM2HwP6/H3sEgrSQdkCaxppqFeJtAnB23pRiJB5E/O9Yp7JAAeWBl+gGEgmBFinnTyOv2RN7rcSmMiw==", "dev": true, "requires": { @@ -54,7 +54,7 @@ }, "@babel/helper-split-export-declaration": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz", "integrity": "sha512-aQ7QowtkgKKzPGf0j6u77kBMdUFVBKNHw2p/3HX/POt5/oz8ec5cs0GwlgM8Hz7ui5EwJnzyfRmkNF1Nx1N7aA==", "dev": true, "requires": { @@ -63,7 +63,7 @@ }, "@babel/highlight": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0-beta.44.tgz", "integrity": "sha512-Il19yJvy7vMFm8AVAh6OZzaFoAd0hbkeMZiX3P5HGD+z7dyI7RzndHB0dg6Urh/VAFfHtpOIzDUSxmY6coyZWQ==", "dev": true, "requires": { @@ -92,16 +92,10 @@ "supports-color": "^5.3.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -111,7 +105,7 @@ }, "@babel/template": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.44.tgz", "integrity": "sha512-w750Sloq0UNifLx1rUqwfbnC6uSUk0mfwwgGRfdLiaUzfAOiH0tHJE6ILQIUi3KYkjiCDTskoIsnfqZvWLBDng==", "dev": true, "requires": { @@ -123,7 +117,7 @@ "dependencies": { "babylon": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", "dev": true } @@ -131,7 +125,7 @@ }, "@babel/traverse": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.44.tgz", "integrity": "sha512-UHuDz8ukQkJCDASKHf+oDt3FVUzFd+QYfuBIsiNu/4+/ix6pP/C+uQZJ6K1oEfbCMv/IKWbgDEh7fcsnIE5AtA==", "dev": true, "requires": { @@ -149,30 +143,36 @@ "dependencies": { "babylon": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", "dev": true }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", + "dev": true + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } } }, "@babel/types": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.44.tgz", "integrity": "sha512-5eTV4WRmqbaFM3v9gHAIljEQJU4Ssc6fxL61JN+Oe2ga/BwyjzjamwkCVVAQjHGuAX8i0BWo42dshL8eO5KfLQ==", "dev": true, "requires": { @@ -209,286 +209,237 @@ "es6-promise": "^2.0.1" } }, + "@sinonjs/commons": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.3.0.tgz", + "integrity": "sha512-j4ZwhaHmwsCb4DlDOIWnI5YyKDNMoNThsmwEpfHx6a1EpsGZ9qYLxP++LMlmBRjtGptGHFsGItJ768snllFWpA==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, "@sinonjs/formatio": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-2.0.0.tgz", - "integrity": "sha512-ls6CAMA6/5gG+O/IdsBcblvnd8qcO/l1TYoNeAzp3wcISOxlPXQEus0mLcdwazEkWjaBdaJ3TaxmNgCLWwvWzg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.0.0.tgz", + "integrity": "sha512-vdjoYLDptCgvtJs57ULshak3iJe4NW3sJ3g36xVDGff5AE8P30S6A093EIEPjdi2noGhfuNOEkbxt3J3awFW1w==", "dev": true, "requires": { - "samsam": "1.3.0" + "@sinonjs/samsam": "2.1.0" + }, + "dependencies": { + "@sinonjs/samsam": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.1.0.tgz", + "integrity": "sha512-5x2kFgJYupaF1ns/RmharQ90lQkd2ELS8A9X0ymkAAdemYHGtI2KiUHG8nX2WU0T1qgnOU5YMqnBM2V7NUanNw==", + "dev": true, + "requires": { + "array-from": "^2.1.1" + } + } } }, "@sinonjs/samsam": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.0.0.tgz", - "integrity": "sha512-D7VxhADdZbDJ0HjUTMnSQ5xIGb4H2yWpg8k9Sf1T08zfFiQYlaxM8LZydpR4FQ2E6LZJX8IlabNZ5io4vdChwg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.1.2.tgz", + "integrity": "sha512-ZwTHAlC9akprWDinwEPD4kOuwaYZlyMwVJIANsKNC3QVp0AHB04m7RnB4eqeWfgmxw8MGTzS9uMaw93Z3QcZbw==", "dev": true }, "@types/node": { - "version": "10.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.2.tgz", - "integrity": "sha512-m9zXmifkZsMHZBOyxZWilMwmTlpC8x5Ty360JKTiXvlXZfBWYpsg9ZZvP/Ye+iZUh+Q+MxDLjItVTWIsfwz+8Q==", + "version": "10.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.2.tgz", + "integrity": "sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ==", "dev": true }, "@webassemblyjs/ast": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.5.13.tgz", - "integrity": "sha512-49nwvW/Hx9i+OYHg+mRhKZfAlqThr11Dqz8TsrvqGKMhdI2ijy3KBJOun2Z4770TPjrIJhR6KxChQIDaz8clDA==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", + "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", "dev": true, "requires": { - "@webassemblyjs/helper-module-context": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/wast-parser": "1.5.13", - "debug": "^3.1.0", - "mamacro": "^0.0.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/wast-parser": "1.7.11" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz", - "integrity": "sha512-vrvvB18Kh4uyghSKb0NTv+2WZx871WL2NzwMj61jcq2bXkyhRC+8Q0oD7JGVf0+5i/fKQYQSBCNMMsDMRVAMqA==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", + "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz", - "integrity": "sha512-dBh2CWYqjaDlvMmRP/kudxpdh30uXjIbpkLj9HQe+qtYlwvYjPRjdQXrq1cTAAOUSMTtzqbXIxEdEZmyKfcwsg==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", + "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz", - "integrity": "sha512-v7igWf1mHcpJNbn4m7e77XOAWXCDT76Xe7Is1VQFXc4K5jRcFrl9D0NrqM4XifQ0bXiuTSkTKMYqDxu5MhNljA==", - "dev": true, - "requires": { - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", + "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", + "dev": true }, "@webassemblyjs/helper-code-frame": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz", - "integrity": "sha512-yN6ScQQDFCiAXnVctdVO/J5NQRbwyTbQzsGzEgXsAnrxhjp0xihh+nNHQTMrq5UhOqTb5LykpJAvEv9AT0jnAQ==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", + "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", "dev": true, "requires": { - "@webassemblyjs/wast-printer": "1.5.13" + "@webassemblyjs/wast-printer": "1.7.11" } }, "@webassemblyjs/helper-fsm": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz", - "integrity": "sha512-hSIKzbXjVMRvy3Jzhgu+vDd/aswJ+UMEnLRCkZDdknZO3Z9e6rp1DAs0tdLItjCFqkz9+0BeOPK/mk3eYvVzZg==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", + "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", "dev": true }, "@webassemblyjs/helper-module-context": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz", - "integrity": "sha512-zxJXULGPLB7r+k+wIlvGlXpT4CYppRz8fLUM/xobGHc9Z3T6qlmJD9ySJ2jknuktuuiR9AjnNpKYDECyaiX+QQ==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "mamacro": "^0.0.3" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", + "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", + "dev": true }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz", - "integrity": "sha512-0n3SoNGLvbJIZPhtMFq0XmmnA/YmQBXaZKQZcW8maGKwLpVcgjNrxpFZHEOLKjXJYVN5Il8vSfG7nRX50Zn+aw==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", + "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz", - "integrity": "sha512-IJ/goicOZ5TT1axZFSnlAtz4m8KEjYr12BNOANAwGFPKXM4byEDaMNXYowHMG0yKV9a397eU/NlibFaLwr1fbw==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", + "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-buffer": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/wasm-gen": "1.5.13", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11" } }, "@webassemblyjs/ieee754": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz", - "integrity": "sha512-TseswvXEPpG5TCBKoLx9tT7+/GMACjC1ruo09j46ULRZWYm8XHpDWaosOjTnI7kr4SRJFzA6MWoUkAB+YCGKKg==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", + "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", "dev": true, "requires": { - "ieee754": "^1.1.11" + "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.5.13.tgz", - "integrity": "sha512-0NRMxrL+GG3eISGZBmLBLAVjphbN8Si15s7jzThaw1UE9e5BY1oH49/+MA1xBzxpf1OW5sf9OrPDOclk9wj2yg==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", + "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", "dev": true, "requires": { - "long": "4.0.0" - }, - "dependencies": { - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", - "dev": true - } + "@xtuc/long": "4.2.1" } }, "@webassemblyjs/utf8": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.5.13.tgz", - "integrity": "sha512-Ve1ilU2N48Ew0lVGB8FqY7V7hXjaC4+PeZM+vDYxEd+R2iQ0q+Wb3Rw8v0Ri0+rxhoz6gVGsnQNb4FjRiEH/Ng==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", + "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz", - "integrity": "sha512-X7ZNW4+Hga4f2NmqENnHke2V/mGYK/xnybJSIXImt1ulxbCOEs/A+ZK/Km2jgihjyVxp/0z0hwIcxC6PrkWtgw==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-buffer": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/helper-wasm-section": "1.5.13", - "@webassemblyjs/wasm-gen": "1.5.13", - "@webassemblyjs/wasm-opt": "1.5.13", - "@webassemblyjs/wasm-parser": "1.5.13", - "@webassemblyjs/wast-printer": "1.5.13", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", + "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/helper-wasm-section": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11", + "@webassemblyjs/wasm-opt": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", + "@webassemblyjs/wast-printer": "1.7.11" } }, "@webassemblyjs/wasm-gen": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz", - "integrity": "sha512-yfv94Se8R73zmr8GAYzezFHc3lDwE/lBXQddSiIZEKZFuqy7yWtm3KMwA1uGbv5G1WphimJxboXHR80IgX1hQA==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", + "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/ieee754": "1.5.13", - "@webassemblyjs/leb128": "1.5.13", - "@webassemblyjs/utf8": "1.5.13" + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/ieee754": "1.7.11", + "@webassemblyjs/leb128": "1.7.11", + "@webassemblyjs/utf8": "1.7.11" } }, "@webassemblyjs/wasm-opt": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz", - "integrity": "sha512-IkXSkgzVhQ0QYAdIayuCWMmXSYx0dHGU8Ah/AxJf1gBvstMWVnzJnBwLsXLyD87VSBIcsqkmZ28dVb0mOC3oBg==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", + "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-buffer": "1.5.13", - "@webassemblyjs/wasm-gen": "1.5.13", - "@webassemblyjs/wasm-parser": "1.5.13", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-buffer": "1.7.11", + "@webassemblyjs/wasm-gen": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11" } }, "@webassemblyjs/wasm-parser": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz", - "integrity": "sha512-XnYoIcu2iqq8/LrtmdnN3T+bRjqYFjRHqWbqK3osD/0r/Fcv4d9ecRzjVtC29ENEuNTK4mQ9yyxCBCbK8S/cpg==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", + "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-api-error": "1.5.13", - "@webassemblyjs/helper-wasm-bytecode": "1.5.13", - "@webassemblyjs/ieee754": "1.5.13", - "@webassemblyjs/leb128": "1.5.13", - "@webassemblyjs/utf8": "1.5.13" + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-api-error": "1.7.11", + "@webassemblyjs/helper-wasm-bytecode": "1.7.11", + "@webassemblyjs/ieee754": "1.7.11", + "@webassemblyjs/leb128": "1.7.11", + "@webassemblyjs/utf8": "1.7.11" } }, "@webassemblyjs/wast-parser": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz", - "integrity": "sha512-Lbz65T0LQ1LgzKiUytl34CwuhMNhaCLgrh0JW4rJBN6INnBB8NMwUfQM+FxTnLY9qJ+lHJL/gCM5xYhB9oWi4A==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", + "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/floating-point-hex-parser": "1.5.13", - "@webassemblyjs/helper-api-error": "1.5.13", - "@webassemblyjs/helper-code-frame": "1.5.13", - "@webassemblyjs/helper-fsm": "1.5.13", - "long": "^3.2.0", - "mamacro": "^0.0.3" + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/floating-point-hex-parser": "1.7.11", + "@webassemblyjs/helper-api-error": "1.7.11", + "@webassemblyjs/helper-code-frame": "1.7.11", + "@webassemblyjs/helper-fsm": "1.7.11", + "@xtuc/long": "4.2.1" } }, "@webassemblyjs/wast-printer": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz", - "integrity": "sha512-QcwogrdqcBh8Z+eUF8SG+ag5iwQSXxQJELBEHmLkk790wgQgnIMmntT2sMAMw53GiFNckArf5X0bsCA44j3lWQ==", + "version": "1.7.11", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", + "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/wast-parser": "1.5.13", - "long": "^3.2.0" + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/wast-parser": "1.7.11", + "@xtuc/long": "4.2.1" } }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", + "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, "JSONSelect": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/JSONSelect/-/JSONSelect-0.2.1.tgz", @@ -502,9 +453,9 @@ "dev": true }, "acorn": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", - "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz", + "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==", "dev": true }, "acorn-dynamic-import": { @@ -514,25 +465,22 @@ "dev": true, "requires": { "acorn": "^5.0.0" - } - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "^3.0.4" }, "dependencies": { "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", "dev": true } } }, + "acorn-jsx": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.0.tgz", + "integrity": "sha512-XkB50fn0MURDyww9+UYL3c1yLbOBz0ZFvrdYlGB8l+Ije1oSC75qAqrzSPjYQbdnQUzhlUGNKuesryAv0gxZOg==", + "dev": true + }, "adbkit": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/adbkit/-/adbkit-2.11.0.tgz", @@ -550,7 +498,7 @@ "dependencies": { "bluebird": { "version": "2.9.34", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", + "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-2.9.34.tgz", "integrity": "sha1-L3tOyAIWMoqf3evfacjUlC/v99g=", "dev": true } @@ -572,68 +520,79 @@ } }, "addons-linter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-1.0.0.tgz", - "integrity": "sha512-6cC0Jcf0vMVgE86TV4A1lfjx78Fp0JltA1UH2icz/cALWMMfDZrD7YpB4dMrpobclsbKFPHexNOYhS4efAc5ng==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-1.3.1.tgz", + "integrity": "sha512-Oaj8q8hXWwGhrzlMTM7LUxj5ZUxi8k8/pg0V/NlA3usgClngl7jXW4GRlobdoOao8KEnW95y/WNNMeoTbxYe4w==", "dev": true, "requires": { - "ajv": "6.5.0", - "ajv-merge-patch": "3.0.0", + "ajv": "6.5.2", + "ajv-merge-patch": "4.1.0", "babel-register": "6.26.0", "chalk": "2.4.0", "cheerio": "1.0.0-rc.2", "columnify": "1.5.4", - "common-tags": "1.7.2", + "common-tags": "1.8.0", "crx-parser": "0.1.2", - "deepmerge": "2.1.0", - "dispensary": "0.18.0", - "doctoc": "1.3.1", + "deepmerge": "2.1.1", + "dispensary": "0.22.0", "es6-promisify": "5.0.0", - "eslint": "4.19.1", + "eslint": "5.0.1", "eslint-plugin-no-unsafe-innerhtml": "1.0.16", + "eslint-visitor-keys": "1.0.0", + "espree": "4.0.0", "esprima": "3.1.3", "first-chunk-stream": "2.0.0", "fluent-syntax": "^0.7.0", + "fsevents": "1.2.4", "glob": "7.1.2", "is-mergeable-object": "1.1.0", "jed": "1.1.1", - "os-locale": "2.1.0", - "pino": "4.16.1", + "os-locale": "3.0.0", + "pino": "5.0.4", "po2json": "0.4.5", - "postcss": "6.0.19", + "postcss": "6.0.23", "probe-image-size": "4.0.0", "relaxed-json": "1.0.1", "semver": "5.5.0", - "shelljs": "0.8.1", - "snyk": "^1.78.1", + "shelljs": "0.8.2", + "snyk": "^1.88.2", "source-map-support": "0.5.6", "strip-bom-stream": "3.0.0", "tosource": "1.0.0", - "upath": "1.0.5", - "whatwg-url": "6.4.1", + "upath": "1.1.0", + "whatwg-url": "6.5.0", "xmldom": "0.1.27", - "yargs": "11.0.0", - "yauzl": "2.9.1" + "yargs": "12.0.1", + "yauzl": "2.9.2" }, "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-jsx": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", + "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", + "dev": true, + "requires": { + "acorn": "^5.0.3" + } + }, "ajv": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.0.tgz", - "integrity": "sha512-VDUX1oSajablmiyFyED9L1DFndg0P9h7p1F+NO8FkIzei6EPrR6Zu1n18rd5P8PqaSRd/FrWv3G1TVBqpM83gA==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", + "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0", + "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.1" } }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -654,109 +613,251 @@ "supports-color": "^5.3.0" } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, "eslint": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-4.19.1.tgz", - "integrity": "sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.0.1.tgz", + "integrity": "sha512-D5nG2rErquLUstgUaxJlWB5+gu+U/3VDY0fk/Iuq8y9CUFy/7Y6oF4N2cR1tV8knzQvciIbfqfohd359xTLIKQ==", "dev": true, "requires": { - "ajv": "^5.3.0", - "babel-code-frame": "^6.22.0", + "ajv": "^6.5.0", + "babel-code-frame": "^6.26.0", "chalk": "^2.1.0", - "concat-stream": "^1.6.0", - "cross-spawn": "^5.1.0", + "cross-spawn": "^6.0.5", "debug": "^3.1.0", "doctrine": "^2.1.0", - "eslint-scope": "^3.7.1", + "eslint-scope": "^4.0.0", "eslint-visitor-keys": "^1.0.0", - "espree": "^3.5.4", - "esquery": "^1.0.0", + "espree": "^4.0.0", + "esquery": "^1.0.1", "esutils": "^2.0.2", "file-entry-cache": "^2.0.0", "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", - "globals": "^11.0.1", + "globals": "^11.5.0", "ignore": "^3.3.3", "imurmurhash": "^0.1.4", - "inquirer": "^3.0.6", - "is-resolvable": "^1.0.0", - "js-yaml": "^3.9.1", + "inquirer": "^5.2.0", + "is-resolvable": "^1.1.0", + "js-yaml": "^3.11.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", - "lodash": "^4.17.4", - "minimatch": "^3.0.2", + "lodash": "^4.17.5", + "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", "optionator": "^0.8.2", "path-is-inside": "^1.0.2", "pluralize": "^7.0.0", "progress": "^2.0.0", - "regexpp": "^1.0.1", + "regexpp": "^1.1.0", "require-uncached": "^1.0.3", - "semver": "^5.3.0", + "semver": "^5.5.0", + "string.prototype.matchall": "^2.0.0", "strip-ansi": "^4.0.0", - "strip-json-comments": "~2.0.1", - "table": "4.0.2", - "text-table": "~0.2.0" + "strip-json-comments": "^2.0.1", + "table": "^4.0.3", + "text-table": "^0.2.0" + } + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "espree": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", + "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", + "dev": true, + "requires": { + "acorn": "^5.6.0", + "acorn-jsx": "^4.1.1" + } + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" }, "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true } } }, - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "external-editor": { + "version": "2.2.0", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "globals": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", "dev": true }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "inquirer": { + "version": "5.2.0", + "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", "dev": true }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "mem": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", + "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, - "has-flag": { + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "regexpp": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", + "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "dev": true + }, + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "semver": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, "source-map": { @@ -776,63 +877,37 @@ } }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } }, "table": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", - "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", + "version": "4.0.3", + "resolved": "http://registry.npmjs.org/table/-/table-4.0.3.tgz", + "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", "dev": true, "requires": { - "ajv": "^5.2.3", - "ajv-keywords": "^2.1.0", + "ajv": "^6.0.1", + "ajv-keywords": "^3.0.0", "chalk": "^2.1.0", "lodash": "^4.17.4", "slice-ansi": "1.0.0", "string-width": "^2.1.1" - }, - "dependencies": { - "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", - "dev": true - } } }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, "yargs": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", + "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", "get-caller-file": "^1.0.1", "os-locale": "^2.0.0", "require-directory": "^2.1.1", @@ -840,8 +915,21 @@ "set-blocking": "^2.0.0", "string-width": "^2.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" + }, + "dependencies": { + "os-locale": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "dev": true, + "requires": { + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" + } + } } } } @@ -862,15 +950,15 @@ } }, "ajv": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", - "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.5.tgz", + "integrity": "sha512-7q7gtRQDJSyuEHjuVgHoUa2VuemFiCMrfQc9Tc08XTAc4Zj/5U1buQJ0HU6i7fKjXU09SVgSmxa4sLvuvS8Iyg==", "dev": true, "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", + "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, "ajv-keywords": { @@ -880,24 +968,15 @@ "dev": true }, "ajv-merge-patch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-merge-patch/-/ajv-merge-patch-3.0.0.tgz", - "integrity": "sha1-dvBx45H0Gf6f4/6n6SChrYJLK2E=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ajv-merge-patch/-/ajv-merge-patch-4.1.0.tgz", + "integrity": "sha512-0mAYXMSauA8RZ7r+B4+EAOYcZEcO9OK5EiQCR7W7Cv4E44pJj56ZnkKLJ9/PAcOc0dT+LlV9fdDcq2TxVJfOYw==", "dev": true, "requires": { - "fast-json-patch": "^1.0.0", + "fast-json-patch": "^2.0.6", "json-merge-patch": "^0.2.3" } }, - "anchor-markdown-header": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/anchor-markdown-header/-/anchor-markdown-header-0.5.7.tgz", - "integrity": "sha1-BFBj125qH5zTJ6V6ASaqD97Dcac=", - "dev": true, - "requires": { - "emoji-regex": "~6.1.0" - } - }, "ansi-align": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", @@ -909,7 +988,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1027,6 +1106,12 @@ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", "dev": true }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, "array-filter": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz", @@ -1085,10 +1170,13 @@ "dev": true }, "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } }, "asn1.js": { "version": "4.10.1", @@ -1118,7 +1206,7 @@ }, "util": { "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1134,9 +1222,9 @@ "dev": true }, "assertion-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "assign-symbols": { @@ -1146,14 +1234,14 @@ "dev": true }, "ast-types": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", - "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", + "version": "0.11.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.6.tgz", + "integrity": "sha512-nHiuV14upVGl7MWwFUYbzJ6YlfwWS084CU9EA8HajfYQjMSli5TQi3UTRygGF58LFWVkXxS1rbgRhROEqlQkXg==", "dev": true }, "async": { "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-0.2.10.tgz", "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "dev": true }, @@ -1170,9 +1258,9 @@ "dev": true }, "atob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", - "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, "aws-sign2": { @@ -1182,9 +1270,9 @@ "dev": true }, "aws4": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", - "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, "babel-code-frame": { @@ -1226,22 +1314,22 @@ } }, "babel-eslint": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.5.tgz", - "integrity": "sha512-TcdEGCHHquOPQOlH6Fe6MLwPWWWJLdeKhcGoLfOTShETpoH8XYWhjWJw38KCKaTca7c/EdxLolnbakixKxnXDg==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.6.tgz", + "integrity": "sha512-aCdHjhzcILdP8c9lej7hvXKvQieyRt20SF102SIGyY4cUIiw6UaAtK4j2o3dXX74jEmy0TJ0CEhv4fTIM3SzcA==", "dev": true, "requires": { "@babel/code-frame": "7.0.0-beta.44", "@babel/traverse": "7.0.0-beta.44", "@babel/types": "7.0.0-beta.44", "babylon": "7.0.0-beta.44", - "eslint-scope": "~3.7.1", + "eslint-scope": "3.7.1", "eslint-visitor-keys": "^1.0.0" }, "dependencies": { "babylon": { "version": "7.0.0-beta.44", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", + "resolved": "http://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.44.tgz", "integrity": "sha512-5Hlm13BJVAioCHpImtFqNOF2H3ieTOHd0fmFGMxOJ9jgeFqeAwsv3u5P5cR7CSeFrkgHsT19DgFJkHV0/Mcd8g==", "dev": true } @@ -1771,12 +1859,6 @@ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, - "bail": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.3.tgz", - "integrity": "sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg==", - "dev": true - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1845,9 +1927,9 @@ } }, "base64-js": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.2.tgz", - "integrity": "sha1-Ak8Pcq+iW3X5wO5zzU9V7Bvtl4Q=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", "dev": true }, "bcrypt-pbkdf": { @@ -1855,7 +1937,6 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "dev": true, - "optional": true, "requires": { "tweetnacl": "^0.14.3" } @@ -1867,14 +1948,14 @@ "dev": true }, "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", + "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", "dev": true }, "bl": { "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", + "resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz", "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", "dev": true, "requires": { @@ -1883,9 +1964,9 @@ } }, "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz", + "integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==", "dev": true }, "bn.js": { @@ -1900,31 +1981,6 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, - "bops": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/bops/-/bops-0.1.1.tgz", - "integrity": "sha1-Bi4CqNqoAfoQ8uXb5nQM/4Af4X4=", - "dev": true, - "requires": { - "base64-js": "0.0.2", - "to-utf8": "0.0.1" - } - }, - "boundary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/boundary/-/boundary-1.0.1.tgz", - "integrity": "sha1-TWfcJgLAzBbdm85+v4fpSCkPWBI=", - "dev": true - }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -1960,16 +2016,10 @@ "supports-color": "^5.3.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -2030,7 +2080,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2067,7 +2117,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2100,22 +2150,13 @@ } }, "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.2.1.tgz", + "integrity": "sha512-c+Ko0loDaFfuPWiL02ls9Xd3GO3cPVmUobQ6t3rXNUk304u6hGq+8N/kFi+QEIKhzK3uwolVhLzszmfLmMLnqg==", "dev": true, "requires": { "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - }, - "dependencies": { - "base64-js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true - } + "ieee754": "^1.1.4" } }, "buffer-alloc": { @@ -2153,9 +2194,9 @@ "dev": true }, "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", "dev": true }, "buffer-xor": { @@ -2196,7 +2237,7 @@ }, "cacache": { "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", + "resolved": "http://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { @@ -2254,9 +2295,9 @@ "dev": true }, "capture-stack-trace": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", - "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", "dev": true }, "caseless": { @@ -2265,24 +2306,18 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", "dev": true }, - "ccount": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.3.tgz", - "integrity": "sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==", - "dev": true - }, "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", "dev": true, "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" + "pathval": "^1.1.0", + "type-detect": "^4.0.5" } }, "chalk": { @@ -2309,34 +2344,10 @@ } } }, - "character-entities": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.2.tgz", - "integrity": "sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ==", - "dev": true - }, - "character-entities-html4": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.2.tgz", - "integrity": "sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw==", - "dev": true - }, - "character-entities-legacy": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz", - "integrity": "sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA==", - "dev": true - }, - "character-reference-invalid": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz", - "integrity": "sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ==", - "dev": true - }, "chardet": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", - "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, "check-error": { @@ -2381,9 +2392,9 @@ } }, "chownr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", - "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true }, "chrome-trace-event": { @@ -2496,12 +2507,6 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, - "collapse-white-space": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.4.tgz", - "integrity": "sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw==", - "dev": true - }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -2513,18 +2518,18 @@ } }, "color-convert": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.2.tgz", - "integrity": "sha512-3NUJZdhMhcdPn8vJ9v2UQJoH0qqoGUkYTgFEPZaPjEtwmmKUfNV46zZmgB2M5M4DCEQHMaCfWHCxiBflLm04Tg==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "requires": { - "color-name": "1.1.1" + "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha1-SxQVMEz1ACjqgWQ2Q72C6gWANok=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "colors": { @@ -2545,7 +2550,7 @@ "dependencies": { "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -2555,28 +2560,25 @@ } }, "combined-stream": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", - "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", "dev": true, "requires": { "delayed-stream": "~1.0.0" } }, "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", "dev": true }, "common-tags": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.7.2.tgz", - "integrity": "sha512-joj9ZlUOjCrwdbmiLqafeUSgkUM74NqhLsZtSqDmhKudaIY197zTrb8JMl31fMnCUuxwFT23eC/oWvrZzDLRJQ==", - "dev": true, - "requires": { - "babel-runtime": "^6.26.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", + "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==", + "dev": true }, "commondir": { "version": "1.0.1", @@ -2676,9 +2678,9 @@ "dev": true }, "copy-webpack-plugin": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.5.2.tgz", - "integrity": "sha512-zmC33E8FFSq3AbflTvqvPvBo621H36Afsxlui91d+QyZxPIuXghfnTsa1CuqiAaCPgJoSUWfTFbKJnadZpKEbQ==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", + "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", "dev": true, "requires": { "cacache": "^10.0.4", @@ -2704,10 +2706,13 @@ "dev": true }, "crc": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.5.0.tgz", - "integrity": "sha1-mLi6fUiWZbo5efWbITgTdBAaGWQ=", - "dev": true + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "requires": { + "buffer": "^5.1.0" + } }, "crc32-stream": { "version": "2.0.0", @@ -2740,7 +2745,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -2753,7 +2758,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -2766,12 +2771,14 @@ } }, "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", "dev": true, "requires": { - "lru-cache": "^4.0.1", + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } @@ -2782,26 +2789,6 @@ "integrity": "sha1-fu7tnt3JXiLBiTguNGJARKiaWm0=", "dev": true }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "dev": true, - "requires": { - "boom": "5.x.x" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - } - } - }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -2829,7 +2816,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -2840,9 +2827,9 @@ } }, "css-what": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.0.tgz", - "integrity": "sha1-lGfQMsOM+u+58teVASUwYvh/ob0=", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz", + "integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ==", "dev": true }, "cyclist": { @@ -2853,7 +2840,7 @@ }, "d": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz", "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { @@ -2903,14 +2890,6 @@ "dev": true, "requires": { "xregexp": "4.0.0" - }, - "dependencies": { - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", - "dev": true - } } }, "decode-uri-component": { @@ -2953,9 +2932,9 @@ "dev": true }, "deepmerge": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.0.tgz", - "integrity": "sha512-Q89Z26KAfA3lpPGhbF6XMfYAm3jIV3avViy6KOJ2JLzFbeWHOvPQUu5aSJIWXap3gDZC2y1eF5HXEPI2wGqgvw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.1.1.tgz", + "integrity": "sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w==", "dev": true }, "defaults": { @@ -2968,13 +2947,12 @@ } }, "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "object-keys": "^1.0.12" } }, "define-property": { @@ -3074,7 +3052,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -3119,7 +3097,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3139,20 +3117,20 @@ } }, "dispensary": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/dispensary/-/dispensary-0.18.0.tgz", - "integrity": "sha512-ikVxjigcj4leoPgjqYcHV1YhmLEZ5eXMq30CIPTf1BniWrH1C0X35F5iVCa4U2/JXZYcQJyHWniSRSOpM+hiNw==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/dispensary/-/dispensary-0.22.0.tgz", + "integrity": "sha512-iwpIOQ4T+fJ55PAPE4G7b8MubUN8dGyZa78VrD6A+XqSnqs844npoGvpwSEETnn064JaaS4gqLcgAfTGR4p2+g==", "dev": true, "requires": { "array-from": "~2.1.1", "async": "~2.6.0", "natural-compare-lite": "~1.4.0", - "pino": "~4.16.1", - "request": "~2.85.0", + "pino": "~5.0.3", + "request": "~2.88.0", "semver": "~5.5.0", "sha.js": "~2.4.4", "source-map-support": "~0.5.4", - "yargs": "~11.0.0" + "yargs": "~12.0.1" }, "dependencies": { "async": { @@ -3164,10 +3142,53 @@ "lodash": "^4.17.10" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "semver": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", + "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==", "dev": true }, "source-map": { @@ -3177,65 +3198,37 @@ "dev": true }, "source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", "dev": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" - } - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true + } }, "yargs": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz", - "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "os-locale": "^3.0.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^2.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" } } } }, - "doctoc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/doctoc/-/doctoc-1.3.1.tgz", - "integrity": "sha1-8BLjYD4xViVMLvIqyIxxkPVUJro=", - "dev": true, - "requires": { - "anchor-markdown-header": "^0.5.5", - "htmlparser2": "~3.9.2", - "markdown-to-ast": "~3.4.0", - "minimist": "~1.2.0", - "underscore": "~1.8.3", - "update-section": "^0.3.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -3257,7 +3250,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true } @@ -3270,9 +3263,9 @@ "dev": true }, "domelementtype": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", - "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.2.1.tgz", + "integrity": "sha512-SQVCLFS2E7G5CRCMdn6K9bIhRj1bS6QBWZfF0TUPh4V/BbqrQ619IdSS3/izn0FZ+9l+uODzaZjb08fjOfablA==", "dev": true }, "domhandler": { @@ -3320,9 +3313,9 @@ "dev": true }, "duplexify": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", - "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", + "integrity": "sha512-vM58DwdnKmty+FSPzT14K9JXb90H+j5emaR4KYbr2KTIz00WHGbWOe5ghQTx233ZCLZtrGDALzKwcjEtSt35mA==", "dev": true, "requires": { "end-of-stream": "^1.0.0", @@ -3332,13 +3325,13 @@ } }, "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "dev": true, - "optional": true, "requires": { - "jsbn": "~0.1.0" + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, "ecdsa-sig-formatter": { @@ -3351,9 +3344,9 @@ } }, "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "dev": true, "requires": { "bn.js": "^4.4.0", @@ -3371,12 +3364,6 @@ "integrity": "sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ==", "dev": true }, - "emoji-regex": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.1.3.tgz", - "integrity": "sha1-7HmjlpsC0uzytyJUJ5v5m8eoOTI=", - "dev": true - }, "emojis-list": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", @@ -3413,9 +3400,9 @@ } }, "entities": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz", - "integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, "errno": { @@ -3450,20 +3437,20 @@ } }, "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=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "^1.1.1", + "is-callable": "^1.1.4", "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-symbol": "^1.0.2" } }, "es5-ext": { - "version": "0.10.45", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.45.tgz", - "integrity": "sha512-FkfM6Vxxfmztilbxxz5UKSD4ICMf5tSpRFtDNtkAhOxZ0EKtX6qwmXNyH/sFyIbX2P/nU5AMiA9jilWsUGJzCQ==", + "version": "0.10.46", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.46.tgz", + "integrity": "sha512-24XxRvJXNFwEMpJb3nOkiRJKRoupmjYmOPVlI65Qy2SrtxwOTB+g6ODjBKOtwEHbYrhWRty9xxOWLNdClT2djw==", "dev": true, "requires": { "es6-iterator": "~2.0.3", @@ -3504,13 +3491,13 @@ }, "es6-promise": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz", "integrity": "sha1-lu258v2wGZWCKyY92KratnSBgbw=", "dev": true }, "es6-promisify": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { @@ -3518,9 +3505,9 @@ }, "dependencies": { "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz", + "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg==", "dev": true } } @@ -3567,9 +3554,9 @@ "dev": true }, "escodegen": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.10.0.tgz", - "integrity": "sha512-fjUOf8johsv23WuIKdNQU4P9t9jhQ4Qzx6pC2uW890OloK3Zs1ZAoCNpg/2larNF501jLl3UNy0kIRcF6VI22g==", + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", + "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", "dev": true, "requires": { "esprima": "^3.1.3", @@ -3607,16 +3594,16 @@ } }, "eslint": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.1.0.tgz", - "integrity": "sha512-DyH6JsoA1KzA5+OSWFjg56DFJT+sDLO0yokaPZ9qY0UEmYrPA1gEX/G1MnVkmRDsksG4H1foIVz2ZXXM3hHYvw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.8.0.tgz", + "integrity": "sha512-Zok6Bru3y2JprqTNm14mgQ15YQu/SMDkWdnmHfFg770DIUlmMFd/gqqzCHekxzjHZJxXv3tmTpH0C1icaYJsRQ==", "dev": true, "requires": { - "ajv": "^6.5.0", - "babel-code-frame": "^6.26.0", + "@babel/code-frame": "^7.0.0", + "ajv": "^6.5.3", "chalk": "^2.1.0", "cross-spawn": "^6.0.5", - "debug": "^3.1.0", + "debug": "^4.0.1", "doctrine": "^2.1.0", "eslint-scope": "^4.0.0", "eslint-utils": "^1.3.1", @@ -3628,11 +3615,11 @@ "functional-red-black-tree": "^1.0.1", "glob": "^7.1.2", "globals": "^11.7.0", - "ignore": "^3.3.3", + "ignore": "^4.0.6", "imurmurhash": "^0.1.4", - "inquirer": "^5.2.0", + "inquirer": "^6.1.0", "is-resolvable": "^1.1.0", - "js-yaml": "^3.11.0", + "js-yaml": "^3.12.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.3.0", "lodash": "^4.17.5", @@ -3643,35 +3630,33 @@ "path-is-inside": "^1.0.2", "pluralize": "^7.0.0", "progress": "^2.0.0", - "regexpp": "^1.1.0", + "regexpp": "^2.0.1", "require-uncached": "^1.0.3", - "semver": "^5.5.0", - "string.prototype.matchall": "^2.0.0", + "semver": "^5.5.1", "strip-ansi": "^4.0.0", "strip-json-comments": "^2.0.1", - "table": "^4.0.3", + "table": "^5.0.2", "text-table": "^0.2.0" }, "dependencies": { - "acorn-jsx": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-4.1.1.tgz", - "integrity": "sha512-JY+iV6r+cO21KtntVvFkD+iqjtdpRUpGqKWgfkCdZq1R+kbreEl8EcdcJR4SmiIgsIQT33s6QzheQ9a275Q8xw==", + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", "dev": true, "requires": { - "acorn": "^5.0.3" + "@babel/highlight": "^7.0.0" } }, - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" } }, "ansi-styles": { @@ -3694,26 +3679,13 @@ "supports-color": "^5.3.0" } }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz", + "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, "eslint-scope": { @@ -3726,59 +3698,34 @@ "estraverse": "^4.1.1" } }, - "espree": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-4.0.0.tgz", - "integrity": "sha512-kapdTCt1bjmspxStVKX6huolXVV5ZfyZguY1lcfhVVZstce3bqxH9mcLzNn3/mlgW6wQ732+0fuG9v7h0ZQoKg==", - "dev": true, - "requires": { - "acorn": "^5.6.0", - "acorn-jsx": "^4.1.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "globals": { + "version": "11.8.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.8.0.tgz", + "integrity": "sha512-io6LkyPVuzCHBSQV9fmOwxZkUk6nIaGmxheLDgmuFv89j0fm2aqDbIXKAGfzCMHqz3HLF2Zf8WSG6VqMh2qFmA==", "dev": true }, - "globals": { - "version": "11.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.7.0.tgz", - "integrity": "sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==", + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "inquirer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", - "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^2.1.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^5.5.2", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" - } + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -3795,6 +3742,29 @@ "eslint": "^3.7.1" }, "dependencies": { + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-jsx": { + "version": "3.0.1", + "resolved": "http://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", + "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", + "dev": true, + "requires": { + "acorn": "^3.0.4" + }, + "dependencies": { + "acorn": { + "version": "3.3.0", + "resolved": "http://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", + "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", + "dev": true + } + } + }, "ajv": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", @@ -3813,7 +3783,7 @@ }, "ansi-escapes": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", "dev": true }, @@ -3869,6 +3839,16 @@ "user-home": "^2.0.0" } }, + "espree": { + "version": "3.5.4", + "resolved": "http://registry.npmjs.org/espree/-/espree-3.5.4.tgz", + "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "dev": true, + "requires": { + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" + } + }, "figures": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", @@ -3881,7 +3861,7 @@ }, "inquirer": { "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "resolved": "http://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "dev": true, "requires": { @@ -3911,7 +3891,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -3923,7 +3903,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", "dev": true }, @@ -3946,12 +3926,6 @@ "once": "^1.3.0" } }, - "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", - "dev": true - }, "shelljs": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", @@ -3965,7 +3939,7 @@ }, "slice-ansi": { "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "resolved": "http://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", "dev": true }, @@ -3982,7 +3956,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -3991,7 +3965,7 @@ }, "table": { "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", + "resolved": "http://registry.npmjs.org/table/-/table-3.8.3.tgz", "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "requires": { @@ -4061,19 +4035,20 @@ "dev": true }, "espree": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.4.tgz", - "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-4.1.0.tgz", + "integrity": "sha512-I5BycZW6FCVIub93TeVY1s7vjhP9CY6cXCznIRfiig7nRviKZYdRnj/sHEWC6A7WE9RDWOFq9+7OsWSYz8qv2w==", "dev": true, "requires": { - "acorn": "^5.5.0", - "acorn-jsx": "^3.0.0" + "acorn": "^6.0.2", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" } }, "esprima": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", - "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, "esquery": { @@ -4124,7 +4099,7 @@ }, "events": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4139,12 +4114,12 @@ } }, "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", "dev": true, "requires": { - "cross-spawn": "^5.0.1", + "cross-spawn": "^6.0.0", "get-stream": "^3.0.0", "is-stream": "^1.1.0", "npm-run-path": "^2.0.0", @@ -4195,9 +4170,9 @@ } }, "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, "extend-shallow": { @@ -4222,13 +4197,13 @@ } }, "external-editor": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", - "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", "dev": true, "requires": { - "chardet": "^0.4.0", - "iconv-lite": "^0.4.17", + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, @@ -4310,9 +4285,9 @@ "dev": true }, "fast-deep-equal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", - "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", "dev": true }, "fast-json-parse": { @@ -4322,10 +4297,13 @@ "dev": true }, "fast-json-patch": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-1.2.2.tgz", - "integrity": "sha1-03fZfGkR290qHIC/rNoEik+Du/k=", - "dev": true + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.0.7.tgz", + "integrity": "sha512-DQeoEyPYxdTtfmB3yDlxkLyKTdbJ6ABfFGcMynDqjvGhPYLto/pZyb/dG2Nyd/n9CArjEWN9ZST++AFmgzgbGw==", + "dev": true, + "requires": { + "deep-equal": "^1.0.1" + } }, "fast-json-stable-stringify": { "version": "2.0.0", @@ -4339,16 +4317,22 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-redact": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-1.3.0.tgz", + "integrity": "sha512-ci4qKDR8nDzQCQTPw4hviyDFaSwTgSYiqddRh/EslkUQa0otpzM8IGnuG+LwiUE54t4OjU2T7bYKmjtd7632Wg==", + "dev": true + }, "fast-safe-stringify": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-1.2.3.tgz", - "integrity": "sha512-QJYT/i0QYoiZBQ71ivxdyTqkwKkQ0oxACXHYxH2zYHJEgzi2LsbjgvtzTbLi1SZcF190Db2YP7I7eTsU2egOlw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", + "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", "dev": true }, "fd-slicer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", "dev": true, "requires": { "pend": "~1.2.0" @@ -4423,9 +4407,9 @@ } }, "firefox-profile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-1.1.0.tgz", - "integrity": "sha512-wUIE4QeAjwoHvFbomWmXgKyYtV4/oZxDcJG4znxtGGa/0BhKkd3HzeOf3tAsMWPq1ExARZxCRRiNw1BL3FuPqA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/firefox-profile/-/firefox-profile-1.2.0.tgz", + "integrity": "sha512-TTEFfPOkyaz4EWx/5ZDQC1mJAe3a+JgVcchpIfD4Tvx1UspwlTJRJxOYA35x/z2iJcxaF6aW2rdh6oj6qwgd2g==", "dev": true, "requires": { "adm-zip": "~0.4.x", @@ -4450,20 +4434,9 @@ "lodash": "^4.14.0" } }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -4527,12 +4500,6 @@ "for-in": "^1.0.1" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -4540,13 +4507,13 @@ "dev": true }, "form-data": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", - "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "dev": true, "requires": { "asynckit": "^0.4.0", - "combined-stream": "1.0.6", + "combined-stream": "^1.0.6", "mime-types": "^2.1.12" } }, @@ -4576,9 +4543,9 @@ "dev": true }, "fs-extra": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", - "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", "dev": true, "requires": { "graceful-fs": "^4.1.2", @@ -4624,8 +4591,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -4639,23 +4605,21 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -4668,20 +4632,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -4722,7 +4683,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -4737,14 +4698,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -4753,12 +4714,12 @@ "dev": true, "optional": true, "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" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -4773,7 +4734,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "^2.1.0" } }, "ignore-walk": { @@ -4782,7 +4743,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -4791,15 +4752,14 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -4811,9 +4771,8 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -4826,25 +4785,22 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, - "optional": true, "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" } }, "minizlib": { @@ -4853,14 +4809,13 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "mkdirp": { "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -4877,9 +4832,9 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -4888,16 +4843,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -4906,8 +4861,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -4922,8 +4877,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -4932,17 +4887,16 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -4954,9 +4908,8 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -4977,8 +4930,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -4999,10 +4952,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -5019,13 +4972,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -5034,14 +4987,13 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5077,11 +5029,10 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -5090,16 +5041,15 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -5114,13 +5064,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -5135,20 +5085,18 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -5170,7 +5118,7 @@ }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -5185,6 +5133,12 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true + }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "dev": true } } }, @@ -5217,7 +5171,7 @@ "dependencies": { "commander": { "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "resolved": "http://registry.npmjs.org/commander/-/commander-2.9.0.tgz", "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", "dev": true, "requires": { @@ -5230,6 +5184,12 @@ "integrity": "sha1-NvPiLmB1CSD15yQaR2qMakInWtA=", "dev": true }, + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, "which": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/which/-/which-1.2.4.tgz", @@ -5243,10 +5203,13 @@ } }, "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", + "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", + "dev": true, + "requires": { + "is-property": "^1.0.2" + } }, "generate-object-property": { "version": "1.2.0", @@ -5258,9 +5221,9 @@ } }, "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, "get-func-name": { @@ -5271,7 +5234,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5324,6 +5287,12 @@ "shelljs": "0.7.7" }, "dependencies": { + "graceful-fs": { + "version": "4.1.11", + "resolved": "http://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, "shelljs": { "version": "0.7.7", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz", @@ -5382,9 +5351,9 @@ } }, "global-modules-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.1.0.tgz", - "integrity": "sha512-3DrmGj2TP+96cABk9TfMp6f3knH/Y46dqvWznTU3Tf6/bDGLDAn15tFluQ7BcloykOcdY16U0WGq0BQblYOxJQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/global-modules-path/-/global-modules-path-2.3.0.tgz", + "integrity": "sha512-HchvMJNYh9dGSCy8pOQ2O8u/hoXaL+0XhnrwH0RyLiSXMMTl9W3N6KUU73+JFOg5PGjtzl6VZzUQsnrpm7Szag==", "dev": true }, "globals": { @@ -5409,7 +5378,7 @@ }, "got": { "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz", "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "dev": true, "requires": { @@ -5427,9 +5396,9 @@ } }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", "dev": true }, "graceful-readlink": { @@ -5466,13 +5435,39 @@ "dev": true }, "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", "dev": true, "requires": { - "ajv": "^5.1.0", + "ajv": "^5.3.0", "har-schema": "^2.0.0" + }, + "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + } } }, "has": { @@ -5554,7 +5549,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -5580,18 +5575,6 @@ "minimalistic-assert": "^1.0.1" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "dev": true, - "requires": { - "boom": "4.x.x", - "cryptiles": "3.x.x", - "hoek": "4.x.x", - "sntp": "2.x.x" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -5609,12 +5592,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoek": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", - "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==", - "dev": true - }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -5632,9 +5609,9 @@ "dev": true }, "htmlparser2": { - "version": "3.9.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.9.2.tgz", - "integrity": "sha1-G9+HrMoPP55T+k/M6w9LTLsAszg=", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.0.tgz", + "integrity": "sha512-J1nEUGv+MkXS0weHNWVKJJ+UrLfePxRWpN3C9bEi9fLxL2+ggW94DQvgYVXsaT30PGwYRIZKNZXuyMhp3Di4bQ==", "dev": true, "requires": { "domelementtype": "^1.3.0", @@ -5642,12 +5619,31 @@ "domutils": "^1.5.1", "entities": "^1.1.1", "inherits": "^2.0.1", - "readable-stream": "^2.0.2" + "readable-stream": "^3.0.6" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.0", + "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.3.0.tgz", + "integrity": "sha1-sXrtguirWeUt2cGbF1bg/BhyBMI=", + "dev": true + }, + "readable-stream": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.0.6.tgz", + "integrity": "sha512-9E1oLoOWfhSXHGv6QlwXJim7uNzd9EVlWK+21tCU9Ju/kR0/p2AZYPz4qSchgO8PlLIH4FpZYfzwS+rEksZjIg==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } } }, "http-errors": { "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -5706,20 +5702,26 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "iconv-lite": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", - "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -5743,6 +5745,12 @@ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", @@ -5750,13 +5758,67 @@ "dev": true }, "import-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz", - "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", "dev": true, "requires": { - "pkg-dir": "^2.0.0", + "pkg-dir": "^3.0.0", "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } } }, "imurmurhash": { @@ -5794,22 +5856,21 @@ "dev": true }, "inquirer": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", - "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.0.tgz", + "integrity": "sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg==", "dev": true, "requires": { "ansi-escapes": "^3.0.0", "chalk": "^2.0.0", "cli-cursor": "^2.1.0", "cli-width": "^2.0.0", - "external-editor": "^2.0.4", + "external-editor": "^3.0.0", "figures": "^2.0.0", - "lodash": "^4.3.0", + "lodash": "^4.17.10", "mute-stream": "0.0.7", "run-async": "^2.2.0", - "rx-lite": "^4.0.8", - "rx-lite-aggregates": "^4.0.8", + "rxjs": "^6.1.0", "string-width": "^2.1.0", "strip-ansi": "^4.0.0", "through": "^2.3.6" @@ -5835,16 +5896,10 @@ "supports-color": "^5.3.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -5868,9 +5923,9 @@ } }, "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "dev": true }, "ip": { @@ -5897,22 +5952,6 @@ "kind-of": "^3.0.2" } }, - "is-alphabetical": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.2.tgz", - "integrity": "sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg==", - "dev": true - }, - "is-alphanumerical": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz", - "integrity": "sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg==", - "dev": true, - "requires": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - } - }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5936,7 +5975,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -5964,12 +6003,6 @@ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, - "is-decimal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.2.tgz", - "integrity": "sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg==", - "dev": true - }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", @@ -6025,12 +6058,6 @@ "is-extglob": "^2.1.1" } }, - "is-hexadecimal": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz", - "integrity": "sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A==", - "dev": true - }, "is-installed-globally": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", @@ -6054,9 +6081,9 @@ "dev": true }, "is-my-json-valid": { - "version": "2.17.2", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz", - "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.19.0.tgz", + "integrity": "sha512-mG0f/unGX1HZ5ep4uhRaPOS8EkAY8/j6mDRMJrutq4CqhoJWYp7qAlonIPy3TV7p3ju4TK9fo/PbnoksWmsp5Q==", "dev": true, "requires": { "generate-function": "^2.0.0", @@ -6083,7 +6110,7 @@ }, "is-obj": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, @@ -6172,10 +6199,13 @@ "dev": true }, "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } }, "is-typedarray": { "version": "1.0.0", @@ -6267,8 +6297,7 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true + "dev": true }, "jsesc": { "version": "1.3.0", @@ -6298,9 +6327,9 @@ "dev": true }, "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stable-stringify": { @@ -6353,7 +6382,7 @@ }, "jsonwebtoken": { "version": "8.2.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.1.tgz", + "resolved": "http://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.2.1.tgz", "integrity": "sha512-l8rUBr0fqYYwPc8/ZGrue7GiW7vWdZtZqelxo4Sd5lMvuEeCK8/wS54sEo6tJhdZ6hqfutsj6COgC0d1XdbHGw==", "dev": true, "requires": { @@ -6390,18 +6419,62 @@ } }, "jszip": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.6.1.tgz", - "integrity": "sha1-uI86ey5noqBIFSmCx6N1bZxIKPA=", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.5.tgz", + "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", "dev": true, "requires": { - "pako": "~1.0.2" + "core-js": "~2.3.0", + "es6-promise": "~3.0.2", + "lie": "~3.1.0", + "pako": "~1.0.2", + "readable-stream": "~2.0.6" + }, + "dependencies": { + "core-js": { + "version": "2.3.0", + "resolved": "http://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", + "dev": true + }, + "es6-promise": { + "version": "3.0.2", + "resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", + "dev": true + }, + "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=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } } }, "just-extend": { - "version": "1.1.27", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-1.1.27.tgz", - "integrity": "sha512-mJVp13Ix6gFo3SBAy9U/kL+oeZqzlYYYLQBwXVBlVzIsZwBqGREnOro24oC/8s8aox+rJhtZ2DiQof++IrkA+g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-3.0.0.tgz", + "integrity": "sha512-Fu3T6pKBuxjWT/p4DkqGHFRsysc8OauWr4ZRTY9dIx07Y9O0RkoR5jcv28aeD1vuAwhm3nLkDurwLXoALp4DpQ==", "dev": true }, "jwa": { @@ -6459,12 +6532,12 @@ } }, "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", "dev": true, "requires": { - "invert-kv": "^1.0.0" + "invert-kv": "^2.0.0" } }, "levn": { @@ -6477,9 +6550,18 @@ "type-check": "~0.3.2" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "~3.0.5" + } + }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -6501,7 +6583,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, @@ -6517,9 +6599,9 @@ } }, "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.1.tgz", + "integrity": "sha512-By6ZFY7ETWOc9RFaAIb23IjJVcM4dvJC/N57nmdz9RSkMXvAXGI7SyVlAw3v8vjtDRlqThgVDVmTnr9fqMlxkw==", "dev": true }, "loader-utils": { @@ -6544,9 +6626,9 @@ } }, "lodash": { - "version": "4.17.10", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash.assign": { "version": "4.2.0", @@ -6560,6 +6642,12 @@ "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=", "dev": true }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "dev": true + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -6639,21 +6727,9 @@ "dev": true }, "lolex": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.1.tgz", - "integrity": "sha512-Oo2Si3RMKV3+lV5MsSWplDQFoTClz/24S0MMHYcgGWWmFXr6TMlqcqk/l1GtH+d5wLBwNRiqGnwDRMirtFalJw==", - "dev": true - }, - "long": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", - "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", - "dev": true - }, - "longest-streak": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-1.0.0.tgz", - "integrity": "sha1-0GWXxNTDG1LMsfXY+P5xSOr9aWU=", + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==", "dev": true }, "loose-envify": { @@ -6696,12 +6772,6 @@ "pify": "^3.0.0" } }, - "mamacro": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", - "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==", - "dev": true - }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -6717,49 +6787,25 @@ "object-visit": "^1.0.0" } }, - "markdown-table": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-0.4.0.tgz", - "integrity": "sha1-iQwsGzv+g/sA5BKbjkz+ZFJw+dE=", - "dev": true - }, - "markdown-to-ast": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/markdown-to-ast/-/markdown-to-ast-3.4.0.tgz", - "integrity": "sha1-Diy6gTkLBUmpFT7DsNkVthwWS+c=", - "dev": true, - "requires": { - "debug": "^2.1.3", - "remark": "^5.0.1", - "structured-source": "^3.0.2", - "traverse": "^0.6.6" - }, - "dependencies": { - "traverse": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", - "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=", - "dev": true - } - } - }, "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", "dev": true, "requires": { "hash-base": "^3.0.0", - "inherits": "^2.0.1" + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-3.0.1.tgz", + "integrity": "sha512-QKs47bslvOE0NbXOqG6lMxn6Bk0Iuw0vfrIeLykmQle2LkCw1p48dZDdzE+D88b/xqRJcZGcMNeDvSVma+NuIQ==", "dev": true, "requires": { - "mimic-fn": "^1.0.0" + "mimic-fn": "^1.0.0", + "p-is-promise": "^1.1.0" } }, "memory-fs": { @@ -6812,18 +6858,18 @@ } }, "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", + "version": "1.37.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", "dev": true }, "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", + "version": "2.1.21", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", + "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", "dev": true, "requires": { - "mime-db": "~1.33.0" + "mime-db": "~1.37.0" } }, "mimic-fn": { @@ -6997,6 +7043,18 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "multimatch": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-2.1.0.tgz", + "integrity": "sha1-nHkGoi+0wCkZ4vX3UWG0zb1LKis=", + "dev": true, + "requires": { + "array-differ": "^1.0.0", + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "minimatch": "^3.0.0" + } + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -7031,7 +7089,7 @@ }, "rimraf": { "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", "dev": true, "optional": true, @@ -7053,9 +7111,9 @@ } }, "nan": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz", - "integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", "dev": true, "optional": true }, @@ -7112,7 +7170,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, @@ -7139,6 +7197,12 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, "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", @@ -7148,9 +7212,18 @@ "number-is-nan": "^1.0.0" } }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -7170,7 +7243,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -7185,7 +7258,7 @@ }, "yargs": { "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "dev": true, "requires": { @@ -7202,15 +7275,15 @@ }, "ncp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "resolved": "http://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "dev": true, "optional": true }, "needle": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.1.tgz", - "integrity": "sha512-t/ZswCM9JTWjAdXS9VpvqhI2Ct2sL2MdY4fUXqGJaGBk13ge99ObqRksRTbBE56K+wxUXwwfZYOuZHifFW9q+Q==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", + "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", "dev": true, "requires": { "debug": "^2.1.2", @@ -7219,9 +7292,9 @@ } }, "neo-async": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.5.1.tgz", - "integrity": "sha512-3KL3fvuRkZ7s4IFOMfztb7zJp3QaVWnBeGoJlgB38XnCRPj/0tLzzLG5IB8NYOHbJ8g8UGrgZv44GLDk6CxTxA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz", + "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==", "dev": true }, "netmask": { @@ -7232,33 +7305,33 @@ }, "next-tick": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "resolved": "http://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, "nice-try": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.4.tgz", - "integrity": "sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, "nise": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.2.tgz", - "integrity": "sha512-BxH/DxoQYYdhKgVAfqVy4pzXRZELHOIewzoesxpjYvpU+7YOalQhGNPf7wAx8pLrTNPrHRDlLOkAl8UI0ZpXjw==", + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.4.6.tgz", + "integrity": "sha512-1GedetLKzmqmgwabuMSqPsT7oumdR77SBpDfNNJhADRIeA3LN/2RVqR4fFqwvzhAqcTef6PPCzQwITE/YQ8S8A==", "dev": true, "requires": { - "@sinonjs/formatio": "^2.0.0", - "just-extend": "^1.1.27", + "@sinonjs/formatio": "3.0.0", + "just-extend": "^3.0.0", "lolex": "^2.3.2", "path-to-regexp": "^1.7.0", "text-encoding": "^0.6.4" } }, "node-forge": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz", - "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz", + "integrity": "sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==", "dev": true }, "node-libs-browser": { @@ -7292,6 +7365,17 @@ "vm-browserify": "0.0.4" }, "dependencies": { + "buffer": { + "version": "4.9.1", + "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -7330,7 +7414,7 @@ }, "chalk": { "version": "0.4.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", + "resolved": "http://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz", "integrity": "sha1-UZmj3c0MHv4jvAjBsCewYXbgxk8=", "dev": true, "requires": { @@ -7341,15 +7425,9 @@ }, "strip-ansi": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz", "integrity": "sha1-OeipjQRNFQZgq+SmgIrPcLt7yZE=", "dev": true - }, - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", - "dev": true } } }, @@ -7384,9 +7462,9 @@ } }, "nth-check": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.1.tgz", - "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "dev": true, "requires": { "boolbase": "~1.0.0" @@ -7399,9 +7477,9 @@ "dev": true }, "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", "dev": true }, "object-assign": { @@ -7476,7 +7554,7 @@ }, "opn": { "version": "5.3.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "resolved": "http://registry.npmjs.org/opn/-/opn-5.3.0.tgz", "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", "dev": true, "requires": { @@ -7510,14 +7588,14 @@ "dev": true }, "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.0.0.tgz", + "integrity": "sha512-4mi6ZXIp4OtcV/Bwzl9p9Cvae7KJv/czGIm/HK0iaXCuRh7BMpy4l4o4CLjN+atsRQpCW9Rs4FdhfnK0zaR1Jg==", "dev": true, "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" + "execa": "^0.10.0", + "lcid": "^2.0.0", + "mem": "^3.0.1" } }, "os-name": { @@ -7548,6 +7626,12 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", "dev": true }, + "p-is-promise": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -7589,13 +7673,19 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, @@ -7643,7 +7733,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -7654,20 +7744,6 @@ "pbkdf2": "^3.0.3" } }, - "parse-entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.1.2.tgz", - "integrity": "sha512-5N9lmQ7tmxfXf+hO3X6KRG6w7uYO/HL9fHalSySTdyn63C3WNvTM/1R8tn1u1larNcEbo3Slcy2bsVDQqvEpUg==", - "dev": true, - "requires": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -7740,9 +7816,9 @@ "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "path-to-regexp": { @@ -7778,9 +7854,9 @@ "dev": true }, "pbkdf2": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.16.tgz", - "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -7824,72 +7900,37 @@ } }, "pino": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-4.16.1.tgz", - "integrity": "sha512-ST/IC5RMyqrOZL+Hq6LDwz5h4fGKABXzx2/5Ze7rz5TjuPvE8uI72dzj409xkq9JjyWsKoOOApgXn8kEjJ73yg==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/pino/-/pino-5.0.4.tgz", + "integrity": "sha512-w7UohXesFggN77UyTnt0A7FqkEiq6TbeXgTvY7g1wFGXoGbxmF780uFm8oQKaWlFi7vnzDRkBnYHNaaHFUKEoQ==", "dev": true, "requires": { - "chalk": "^2.3.2", "fast-json-parse": "^1.0.3", - "fast-safe-stringify": "^1.2.3", + "fast-redact": "^1.1.14", + "fast-safe-stringify": "^2.0.4", "flatstr": "^1.0.5", - "pino-std-serializers": "^2.0.0", + "pino-std-serializers": "^2.2.0", "pump": "^3.0.0", - "quick-format-unescaped": "^1.1.2", - "split2": "^2.2.0" + "quick-format-unescaped": "^3.0.0", + "sonic-boom": "^0.6.0" }, "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "pump": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } } } }, "pino-std-serializers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.1.0.tgz", - "integrity": "sha512-NqWvrQD/GpY78ybiNBzi/dg8ylERhDo6nB33j5sfCKpUmWLc3lYzeoBjyRoCMvEpDpL9lmH6ufRd0jw6rcd1pQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-2.3.0.tgz", + "integrity": "sha512-klfGoOsP6sJH7ON796G4xoUSx2fkpFgKHO4YVVO2zmz31jR+etzc/QzGJILaOIiCD6HTCFgkPx+XN8nk+ruqPw==", "dev": true }, "pkg-dir": { @@ -7901,12 +7942,6 @@ "find-up": "^2.1.0" } }, - "pkginfo": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", - "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", - "dev": true - }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -7915,7 +7950,7 @@ }, "po2json": { "version": "0.4.5", - "resolved": "https://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", + "resolved": "http://registry.npmjs.org/po2json/-/po2json-0.4.5.tgz", "integrity": "sha1-R7spUtoy1Yob4vJWpZjuvAt0URg=", "dev": true, "requires": { @@ -7930,14 +7965,14 @@ "dev": true }, "postcss": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.19.tgz", - "integrity": "sha512-f13HRz0HtVwVaEuW6J6cOUCBLFtymhgyLPV7t4QEk2UD3twRI9IluDcQNdzQdBpiixkXj2OmzejhhTbSbDxNTg==", + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "^2.3.1", + "chalk": "^2.4.1", "source-map": "^0.6.1", - "supports-color": "^5.2.0" + "supports-color": "^5.4.0" }, "dependencies": { "ansi-styles": { @@ -7960,12 +7995,6 @@ "supports-color": "^5.3.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7973,9 +8002,9 @@ "dev": true }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -8034,9 +8063,9 @@ "dev": true }, "progress": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", - "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", + "integrity": "sha512-OE+a6vzqazc+K6LxJrX5UPyKFvGnL5CYmq2jFGNIBWHpc4QyE49/YOumcrpQFJpfejmvRtbJzgO1zPmMCqlbBg==", "dev": true }, "promise": { @@ -8055,9 +8084,9 @@ "dev": true }, "proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz", - "integrity": "sha512-g6n6vnk8fRf705ShN+FEXFG/SDJaW++lSs0d9KaJh4uBWW/wi7en4Cpo5VYQW3SZzAE121lhB/KLQrbURoubZw==", + "version": "2.3.1", + "resolved": "http://registry.npmjs.org/proxy-agent/-/proxy-agent-2.3.1.tgz", + "integrity": "sha512-CNKuhC1jVtm8KJYFTS2ZRO71VCBx3QSA92So/e6NrY6GoJonkx3Irnk4047EsCcswczwqAekRj3s8qLRGahSKg==", "dev": true, "requires": { "agent-base": "^4.2.0", @@ -8071,13 +8100,19 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, @@ -8099,17 +8134,24 @@ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", "dev": true }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==", + "dev": true + }, "public-encrypt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.2.tgz", - "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", "dev": true, "requires": { "bn.js": "^4.1.0", "browserify-rsa": "^4.0.0", "create-hash": "^1.1.0", "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "pump": { @@ -8158,13 +8200,10 @@ "dev": true }, "quick-format-unescaped": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-1.1.2.tgz", - "integrity": "sha1-DKWB3jF0vs7yWsPC6JVjQjgdtpg=", - "dev": true, - "requires": { - "fast-safe-stringify": "^1.0.8" - } + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-3.0.1.tgz", + "integrity": "sha512-Tnk4iJQ8x3V8ml3x9sLIf4tSDaVB9OJY/5gOrnxgK63CXKphhn8oYOPI4tqnXPQcZ3tCv7GFjeoYY5h6UAvuzg==", + "dev": true }, "randombytes": { "version": "2.0.6", @@ -8195,6 +8234,17 @@ "http-errors": "1.6.3", "iconv-lite": "0.4.23", "unpipe": "1.0.0" + }, + "dependencies": { + "iconv-lite": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", + "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } } }, "rc": { @@ -8211,7 +8261,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -8241,7 +8291,7 @@ }, "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8280,7 +8330,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { @@ -8294,15 +8344,14 @@ } }, "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" } }, "readline2": { @@ -8394,9 +8443,9 @@ } }, "regexpp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-1.1.0.tgz", - "integrity": "sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, "regexpu-core": { @@ -8462,50 +8511,6 @@ "commander": "^2.6.0" } }, - "remark": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/remark/-/remark-5.1.0.tgz", - "integrity": "sha1-y0Y709vLS5l5STXu4c9x16jjBow=", - "dev": true, - "requires": { - "remark-parse": "^1.1.0", - "remark-stringify": "^1.1.0", - "unified": "^4.1.1" - } - }, - "remark-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-1.1.0.tgz", - "integrity": "sha1-w8oQ+ajaBGFcKPCapOMEUQUm7CE=", - "dev": true, - "requires": { - "collapse-white-space": "^1.0.0", - "extend": "^3.0.0", - "parse-entities": "^1.0.2", - "repeat-string": "^1.5.4", - "trim": "0.0.1", - "trim-trailing-lines": "^1.0.0", - "unherit": "^1.0.4", - "unist-util-remove-position": "^1.0.0", - "vfile-location": "^2.0.0" - } - }, - "remark-stringify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-1.1.0.tgz", - "integrity": "sha1-pxBeJbnuK/mkm3XSxCPxGwauIJI=", - "dev": true, - "requires": { - "ccount": "^1.0.0", - "extend": "^3.0.0", - "longest-streak": "^1.0.0", - "markdown-table": "^0.4.0", - "parse-entities": "^1.0.2", - "repeat-string": "^1.5.4", - "stringify-entities": "^1.0.1", - "unherit": "^1.0.4" - } - }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -8513,9 +8518,9 @@ "dev": true }, "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", "dev": true }, "repeat-string": { @@ -8534,33 +8539,31 @@ } }, "request": { - "version": "2.85.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.85.0.tgz", - "integrity": "sha512-8H7Ehijd4js+s6wuVPLjwORxD4zeuyjYugprdOXlPSqaApmL/QOy+EB/beICHVCHkGMKNh5rvihb5ov+IDw4mg==", + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "dev": true, "requires": { "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", + "aws4": "^1.8.0", "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "hawk": "~6.0.2", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "stringstream": "~0.0.5", - "tough-cookie": "~2.3.3", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "uuid": "^3.3.2" } }, "require-directory": { @@ -8677,9 +8680,9 @@ } }, "rx-lite": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", - "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", "dev": true }, "rx-lite-aggregates": { @@ -8692,12 +8695,12 @@ } }, "rxjs": { - "version": "5.5.11", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz", - "integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==", + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.3.3.tgz", + "integrity": "sha512-JTWmoY9tWCs7zvIk/CvRjhjGaOd+OVBM987mxFo+OW66cGpdKjZcpmc74ES1sB//7Kl/PAe8+wEakuhG4pcgOw==", "dev": true, "requires": { - "symbol-observable": "1.0.1" + "tslib": "^1.9.0" } }, "safe-buffer": { @@ -8715,7 +8718,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -8728,12 +8731,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, - "samsam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz", - "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==", - "dev": true - }, "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", @@ -8741,39 +8738,13 @@ "dev": true }, "schema-utils": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz", - "integrity": "sha512-yYrjb9TX2k/J1Y5UNy3KYdZq10xhYcF8nMpAW6o3hy6Q8WSIEf9lJHG/ePnOBfziPM3fvQwfOwa13U/Fh8qTfA==", + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", + "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "dev": true, "requires": { "ajv": "^6.1.0", "ajv-keywords": "^3.1.0" - }, - "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } } }, "secure-keys": { @@ -8783,9 +8754,9 @@ "dev": true }, "semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true }, "semver-diff": { @@ -8809,12 +8780,6 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, "set-value": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", @@ -8852,7 +8817,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -8874,7 +8839,7 @@ "dependencies": { "kind-of": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", "dev": true, "requires": { @@ -8911,9 +8876,9 @@ } }, "shelljs": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.1.tgz", - "integrity": "sha512-YA/iYtZpzFe5HyWVGrb02FjPxc4EMCfpoU/Phg9fQoyMC72u9598OUBrsU8IrtwAKG0tO8IYaqbaLIw+k3IRGA==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.2.tgz", + "integrity": "sha512-pRXeNrCA2Wd9itwhvLp5LZQvPJ0wU6bcjaTMywHHGX5XWhVN2nzSu7WV0q+oUY7mGK3mgSkDDzP3MgjqdyIgbQ==", "dev": true, "requires": { "glob": "^7.0.0", @@ -8945,9 +8910,21 @@ "when": "3.7.7" }, "dependencies": { + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "dev": true, + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, "babel-polyfill": { "version": "6.16.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.16.0.tgz", + "resolved": "http://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.16.0.tgz", "integrity": "sha1-LUUCHfh+JqN0ttTRqcZZZNF/JCI=", "dev": true, "requires": { @@ -8962,6 +8939,28 @@ "integrity": "sha1-8JTHBB9mJZm7EnINoFnWucf/D0A=", "dev": true }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "http://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, "mz": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.5.0.tgz", @@ -8973,6 +8972,18 @@ "thenify-all": "^1.0.0" } }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, "regenerator-runtime": { "version": "0.9.6", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz", @@ -9015,6 +9026,15 @@ "requires": { "source-map": "^0.5.3" } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "dev": true, + "requires": { + "punycode": "^1.4.1" + } } } }, @@ -9025,35 +9045,30 @@ "dev": true }, "sinon": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.1.3.tgz", - "integrity": "sha512-yeTza8xIZZdiXntCHJAzKll/sSYE+DuJOS8hiSapzaLqdW8eCNVVC9je9SZYYTkPm2bLts9x6UYxwuMAVVrM6Q==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.3.5.tgz", + "integrity": "sha512-xgoZ2gKjyVRcF08RrIQc+srnSyY1JDJtxu3Nsz07j1ffjgXoY6uPLf/qja6nDBZgzYYEovVkFryw2+KiZz11xQ==", "dev": true, "requires": { - "@sinonjs/formatio": "^2.0.0", - "@sinonjs/samsam": "^2.0.0", + "@sinonjs/commons": "^1.0.2", + "@sinonjs/formatio": "^3.0.0", + "@sinonjs/samsam": "^2.1.2", "diff": "^3.5.0", "lodash.get": "^4.4.2", - "lolex": "^2.4.2", - "nise": "^1.3.3", - "supports-color": "^5.4.0", + "lolex": "^2.7.5", + "nise": "^1.4.5", + "supports-color": "^5.5.0", "type-detect": "^4.0.8" }, "dependencies": { "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true } } }, @@ -9180,19 +9195,10 @@ "kind-of": "^3.2.0" } }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "dev": true, - "requires": { - "hoek": "4.x.x" - } - }, "snyk": { - "version": "1.88.2", - "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.88.2.tgz", - "integrity": "sha1-//J+u6pPYkOYvW0xt2wAiRLv1rc=", + "version": "1.108.0", + "resolved": "https://registry.npmjs.org/snyk/-/snyk-1.108.0.tgz", + "integrity": "sha512-QKeERkklW4DFyd49sqbwZ4xNYXtHOPCcUjNUzDfcvXzNwyxfRKhTf43nmPw6lnIcgBesrY95hMozos4WmgYl3w==", "dev": true, "requires": { "abbrev": "^1.1.1", @@ -9203,28 +9209,30 @@ "hasbin": "^1.2.3", "inquirer": "^3.0.0", "lodash": "^4.17.5", - "needle": "^2.0.1", + "needle": "^2.2.4", "opn": "^5.2.0", "os-name": "^2.0.1", - "proxy-agent": "^3.0.0", + "proxy-agent": "^2.0.0", "proxy-from-env": "^1.0.0", "recursive-readdir": "^2.2.2", "semver": "^5.5.0", - "snyk-config": "2.1.0", - "snyk-docker-plugin": "1.10.3", - "snyk-go-plugin": "1.5.1", - "snyk-gradle-plugin": "1.3.0", - "snyk-module": "1.8.2", - "snyk-mvn-plugin": "1.2.0", - "snyk-nuget-plugin": "1.6.2", + "snyk-config": "2.2.0", + "snyk-docker-plugin": "1.12.1", + "snyk-go-plugin": "1.6.0", + "snyk-gradle-plugin": "2.1.0", + "snyk-module": "1.9.1", + "snyk-mvn-plugin": "2.0.0", + "snyk-nodejs-lockfile-parser": "1.7.0", + "snyk-nuget-plugin": "1.6.5", "snyk-php-plugin": "1.5.1", - "snyk-policy": "1.12.0", - "snyk-python-plugin": "1.6.1", + "snyk-policy": "1.13.1", + "snyk-python-plugin": "1.9.0", "snyk-resolve": "1.0.1", - "snyk-resolve-deps": "3.1.0", - "snyk-sbt-plugin": "1.3.0", + "snyk-resolve-deps": "4.0.2", + "snyk-sbt-plugin": "2.0.0", "snyk-tree": "^1.0.0", "snyk-try-require": "1.3.1", + "source-map-support": "^0.5.9", "tempfile": "^2.0.0", "then-fs": "^2.0.0", "undefsafe": "^2.0.0", @@ -9251,25 +9259,86 @@ "supports-color": "^5.3.0" } }, + "chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", + "dev": true + }, "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "external-editor": { + "version": "2.2.0", + "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "dev": true, + "requires": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + } + }, + "inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + }, + "rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -9278,82 +9347,64 @@ } }, "snyk-config": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/snyk-config/-/snyk-config-2.1.0.tgz", - "integrity": "sha512-D1Xz1pZa9lwA9AHogmAigyJGo/iuEGH+rcPB77mFsneVfnuiK9c6IjnsHbEBUf1cePtZvWdGBjs6e75Cvc2AMg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/snyk-config/-/snyk-config-2.2.0.tgz", + "integrity": "sha512-mq0wbP/AgjcmRq5i5jg2akVVV3iSYUPTowZwKn7DChRLDL8ySOzWAwan+ImXiyNbrWo87FNI/15O6MpOnTxOIg==", "dev": true, "requires": { "debug": "^3.1.0", + "lodash": "^4.17.5", "nconf": "^0.10.0" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-docker-plugin": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/snyk-docker-plugin/-/snyk-docker-plugin-1.10.3.tgz", - "integrity": "sha512-nIw6zS705SiQLEhBwoO2qsJ3lVN1DZ48tyMgqhlr5f5GuOrwUJ0ivUK5HQUI79xA6pF7tU18495OlbsKuEHUOw==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/snyk-docker-plugin/-/snyk-docker-plugin-1.12.1.tgz", + "integrity": "sha512-9/k+tZORb0CUoE+nFvG+ADc6vzHAkgiGR/7aZ35vxpuc9vW37LFWjmXZAfyoiGNOn1ICrPxSxarah8YsFEwE8Q==", "dev": true, "requires": { - "debug": "^3.1.0", - "fs-extra": "^5.0.0", - "pkginfo": "^0.4.1", - "request": "^2.87.0", - "temp-dir": "^1.0.0" + "debug": "^3", + "tslib": "^1" }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "request": { - "version": "2.87.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", - "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.6.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.1", - "forever-agent": "~0.6.1", - "form-data": "~2.3.1", - "har-validator": "~5.0.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.17", - "oauth-sign": "~0.8.2", - "performance-now": "^2.1.0", - "qs": "~6.5.1", - "safe-buffer": "^5.1.1", - "tough-cookie": "~2.3.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.1.0" + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-go-plugin": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/snyk-go-plugin/-/snyk-go-plugin-1.5.1.tgz", - "integrity": "sha512-8OPJOT05Z/UL5fFSXV6b/A6KjlS1Ahr2gpup1bhXtAGXlUUPyWidqkCIER9fexDXqYWgAoDAdn9YHIvmL/5bfw==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/snyk-go-plugin/-/snyk-go-plugin-1.6.0.tgz", + "integrity": "sha512-E6aYw7XAXSs2wJR3fU+vGQ1lVyjAw8PHIQYQwBwMkTHByhJIWPcu6Hy/jT5LcjJHlhYXlpOuk53HeLVK+kcXrQ==", "dev": true, "requires": { "graphlib": "^2.1.1", @@ -9362,67 +9413,110 @@ } }, "snyk-gradle-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-1.3.0.tgz", - "integrity": "sha512-rKZcPwbDM9zk3pFcO0w77MIKOZTkk5ZBVBkBlTlUiFg+eNOKqPTmw2hBGF5NB4ASQmMnx3uB1C8+hrQ405CthA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/snyk-gradle-plugin/-/snyk-gradle-plugin-2.1.0.tgz", + "integrity": "sha512-9gYJluomFZ5kaww5FoBvp4zUIsr27pEJ12jQJaVf0FJ0BmyYHmbCoxvHdqjCSYS2fVtF+fmPnvw0XKQOIwA1SA==", "dev": true, "requires": { "clone-deep": "^0.3.0" } }, "snyk-module": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-1.8.2.tgz", - "integrity": "sha512-XqhdbZ/CUuJ5gSaYdYfapLqx9qm2Mp6nyRMBCLXe9tJSiohOJsc9fQuUDbdOiRCqpA4BD6WLl+qlwOJmJoszBg==", + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/snyk-module/-/snyk-module-1.9.1.tgz", + "integrity": "sha512-A+CCyBSa4IKok5uEhqT+hV/35RO6APFNLqk9DRRHg7xW2/j//nPX8wTSZUPF8QeRNEk/sX+6df7M1y6PBHGSHA==", "dev": true, "requires": { "debug": "^3.1.0", - "hosted-git-info": "^2.1.4" + "hosted-git-info": "^2.7.1" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-mvn-plugin": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-1.2.0.tgz", - "integrity": "sha512-ieTWhn1MB88gEQ6nUtGCeUKQ6Xoxm+u+QmD9u3zfP1QS5ep9fWt3YYDUQjgUiDTJJy7QyVQdZ/fsz3RECnOA7w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/snyk-mvn-plugin/-/snyk-mvn-plugin-2.0.0.tgz", + "integrity": "sha512-9jAhZhv+7YcqtoQYCYlgMoxK+dWBKlk+wkX27Ebg3vNddNop9q5jZitRXTjsXwfSUZHRt+Ptw1f8vei9kjzZVg==", "dev": true }, + "snyk-nodejs-lockfile-parser": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/snyk-nodejs-lockfile-parser/-/snyk-nodejs-lockfile-parser-1.7.0.tgz", + "integrity": "sha512-57Gnw8o3HQbheb808GRsofnYPaJCbpt7n+zec+C7J/GZE6GJk+WA2u1EPsNQAsfTLQ3rxBwA1Sonhg498T4COA==", + "dev": true, + "requires": { + "@yarnpkg/lockfile": "^1.0.2", + "graphlib": "^2.1.5", + "lodash": "4.17.10", + "source-map-support": "^0.5.7", + "tslib": "^1.9.3", + "uuid": "^3.3.2" + }, + "dependencies": { + "lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", + "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + } + } + }, "snyk-nuget-plugin": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/snyk-nuget-plugin/-/snyk-nuget-plugin-1.6.2.tgz", - "integrity": "sha512-8l8hS85esXyweTFgUFdwnGT94Ts42KcG5fdBX2wYosQkpUMePd+GTT9+64k/GvdH5hqcNt2OvtzW+Uf8JF+pbA==", + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/snyk-nuget-plugin/-/snyk-nuget-plugin-1.6.5.tgz", + "integrity": "sha512-3qIndzkxCxiaGvAwMkqChbChGdwhNePPyfi0WjhC/nJGwecqU3Fb/NeTW7lgyT+xoq/dFnzW0DgBJ4+AyNA2gA==", "dev": true, "requires": { "debug": "^3.1.0", - "es6-promise": "^4.1.1", + "jszip": "^3.1.5", "lodash": "^4.17.10", - "xml2js": "^0.4.17", - "zip": "^1.2.0" + "xml2js": "^0.4.17" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } }, - "es6-promise": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.4.tgz", - "integrity": "sha512-/NdNZVJg+uZgtm9eS3O6lrOLYmQag2DjdEXuPaHlZ6RuVqgqaVZfgYCepEIKsLqwdQArOPtC3XzRLqGGfT8KQQ==", + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", "dev": true } } @@ -9439,48 +9533,60 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-policy": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/snyk-policy/-/snyk-policy-1.12.0.tgz", - "integrity": "sha512-CEioNnDzccHyid7UIVl3bJ1dnG4co4ofI+KxuC1mo0IUXy64gxnBTeVoZF5gVLWbAyxGxSeW8f0+8GmWMHVb7w==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/snyk-policy/-/snyk-policy-1.13.1.tgz", + "integrity": "sha512-l9evS3Yk70xyvajjg+I6Ij7fr7gxpVRMZl0J1xNpWps/IVu4DSGih3aMmXi47VJozr4A/eFyj7R1lIr2GhqJCA==", "dev": true, "requires": { "debug": "^3.1.0", - "email-validator": "^2.0.3", - "js-yaml": "^3.5.3", - "lodash.clonedeep": "^4.3.1", - "semver": "^5.5.0", - "snyk-module": "^1.8.2", + "email-validator": "^2.0.4", + "js-yaml": "^3.12.0", + "lodash.clonedeep": "^4.5.0", + "semver": "^5.6.0", + "snyk-module": "^1.9.1", "snyk-resolve": "^1.0.1", - "snyk-try-require": "^1.1.1", + "snyk-try-require": "^1.3.1", "then-fs": "^2.0.0" }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-python-plugin": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/snyk-python-plugin/-/snyk-python-plugin-1.6.1.tgz", - "integrity": "sha512-6zr5jAB3p/bwMZQxZpdj+aPmioTgHB4DI6JMLInhZupss0x8Ome5YqzVzBbOvUKNrc3KaLtjGrJWcAuxDL6M/g==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/snyk-python-plugin/-/snyk-python-plugin-1.9.0.tgz", + "integrity": "sha512-zlyOHoCpmyVym9AwkboeepzEGrY3gHsM7eWP/nJ85TgCnQO5H5orKm3RL57PNbWRY+BnDmoQQ+udQgjym2+3sg==", "dev": true, "requires": { "tmp": "0.0.33" @@ -9497,31 +9603,38 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-resolve-deps": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/snyk-resolve-deps/-/snyk-resolve-deps-3.1.0.tgz", - "integrity": "sha512-YVAelR+dTpqLgfk6lf6WgOlw+MGmGI0r3/Dny8tUbJJ9uVTHTRAOdZCbUyTFqJG7oEmEZxUwmfjqgAuniYwx8Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/snyk-resolve-deps/-/snyk-resolve-deps-4.0.2.tgz", + "integrity": "sha512-nlw62wiWhGOTw3BD3jVIwrUkRR4iNxEkkO4Y/PWs8BsUWseGu1H6QgLesFXJb3qx7ANJ5UbUCJMgV+eL0Lf9cA==", "dev": true, "requires": { "ansicolors": "^0.3.2", - "debug": "^3.1.0", + "debug": "^3.2.5", "lodash.assign": "^4.2.0", "lodash.assignin": "^4.2.0", + "lodash.clone": "^4.5.0", "lodash.flatten": "^4.4.0", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", "lru-cache": "^4.0.0", - "semver": "^5.1.0", + "semver": "^5.5.1", "snyk-module": "^1.6.0", "snyk-resolve": "^1.0.0", "snyk-tree": "^1.0.0", @@ -9530,24 +9643,27 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, "snyk-sbt-plugin": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/snyk-sbt-plugin/-/snyk-sbt-plugin-1.3.0.tgz", - "integrity": "sha512-SRxPB16392dvN3Qv2RfUcHe0XETLWx2kNIOuoNXvc2Gl6DuPW+X+meDJY7xC/yQhU7bSPPKoM2B7awYaj9i2Bg==", - "dev": true, - "requires": { - "debug": "^2.2.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/snyk-sbt-plugin/-/snyk-sbt-plugin-2.0.0.tgz", + "integrity": "sha512-bOUqsQ1Lysnwfnvf4QQIBfC0M0ZVuhlshTKd7pNwgAJ41YEPJNrPEpzOePl/HfKtwilEEwHh5YHvjYGegEKx0A==", + "dev": true }, "snyk-tree": { "version": "1.0.0", @@ -9571,13 +9687,19 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, @@ -9601,10 +9723,19 @@ "socks": "^1.1.10" } }, + "sonic-boom": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.6.1.tgz", + "integrity": "sha512-3qx6XXDeG+hPNa+jla1H6BMBLcjLl8L8NRERLVeIf/EuPqoqmq4K8owG29Xu7OypT/7/YT/0uKW6YitsKA+nLQ==", + "dev": true, + "requires": { + "flatstr": "^1.0.5" + } + }, "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", "dev": true }, "source-map": { @@ -9652,9 +9783,9 @@ } }, "spdx-correct": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", - "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz", + "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==", "dev": true, "requires": { "spdx-expression-parse": "^3.0.0", @@ -9662,9 +9793,9 @@ } }, "spdx-exceptions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz", - "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", "dev": true }, "spdx-expression-parse": { @@ -9678,14 +9809,14 @@ } }, "spdx-license-ids": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz", - "integrity": "sha512-2+EPwgbnmOIl8HjGBXXMd9NAu02vLjOO1nWw4kmeRDFyHn+M/ETfHxQUK0oXg8ctgVnl9t3rosNVsZ1jG61nDA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz", + "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==", "dev": true }, "split": { "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "resolved": "http://registry.npmjs.org/split/-/split-0.3.3.tgz", "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { @@ -9701,15 +9832,6 @@ "extend-shallow": "^3.0.0" } }, - "split2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", - "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", - "dev": true, - "requires": { - "through2": "^2.0.2" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -9717,9 +9839,9 @@ "dev": true }, "sshpk": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", - "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.2.tgz", + "integrity": "sha512-Ra/OXQtuh0/enyl4ETZAfTaeksa6BXks5ZcjpSUNrjBr0DvrJKX+1fsKDPpT9TBXgHAFsa4510aNVgI8g/+SzA==", "dev": true, "requires": { "asn1": "~0.2.3", @@ -9780,9 +9902,9 @@ } }, "stream-each": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.2.tgz", - "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -9908,24 +10030,6 @@ "safe-buffer": "~5.1.0" } }, - "stringify-entities": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-1.3.2.tgz", - "integrity": "sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A==", - "dev": true, - "requires": { - "character-entities-html4": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-hexadecimal": "^1.0.0" - } - }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "dev": true - }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -9950,144 +10054,78 @@ "dev": true }, "strip-bom-buf": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", - "integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=", - "dev": true, - "requires": { - "is-utf8": "^0.2.1" - } - }, - "strip-bom-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-3.0.0.tgz", - "integrity": "sha1-lWvMXYRDD2klapDtgjdlzYWOFZw=", - "dev": true, - "requires": { - "first-chunk-stream": "^2.0.0", - "strip-bom-buf": "^1.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "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=", - "dev": true - }, - "structured-source": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-3.0.2.tgz", - "integrity": "sha1-3YAkJeD1PcSm56yjdSkBoczaevU=", - "dev": true, - "requires": { - "boundary": "^1.0.1" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "symbol-observable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", - "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", - "dev": true - }, - "table": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.3.tgz", - "integrity": "sha512-S7rnFITmBH1EnyKcvxBh1LjYeQMmnZtCXSEbHcH6S0NoKit24ZuFO/T1vDcLdYsLQkM188PVVhQmzKIuThNkKg==", - "dev": true, - "requires": { - "ajv": "^6.0.1", - "ajv-keywords": "^3.0.0", - "chalk": "^2.1.0", - "lodash": "^4.17.4", - "slice-ansi": "1.0.0", - "string-width": "^2.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=", + "dev": true, + "requires": { + "is-utf8": "^0.2.1" } }, - "tapable": { + "strip-bom-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-3.0.0.tgz", + "integrity": "sha1-lWvMXYRDD2klapDtgjdlzYWOFZw=", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom-buf": "^1.0.0" + } + }, + "strip-eof": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.0.0.tgz", - "integrity": "sha512-dQRhbNQkRnaqauC7WqSJ21EEksgT0fYZX2lqXzGkpo8JNig9zGZTYoMGvyI2nWmXlE2VSVXVDu7wLVGu/mQEsg==", + "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "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=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=", + "dev": true + }, + "table": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/table/-/table-5.1.0.tgz", + "integrity": "sha512-e542in22ZLhD/fOIuXs/8yDZ9W61ltF8daM88rkRNtgTIct+vI2fTnAyu/Db2TCfEcI8i7mjZz6meLq0nW7TYg==", + "dev": true, + "requires": { + "ajv": "^6.5.3", + "lodash": "^4.17.10", + "slice-ansi": "1.0.0", + "string-width": "^2.1.1" + } + }, + "tapable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz", + "integrity": "sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==", "dev": true }, "tar-stream": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.1.tgz", - "integrity": "sha512-IFLM5wp3QrJODQFPm6/to3LJZrONdBY/otxcvDIQzu217zKye6yVR3hhi9lAjrC2Z+m/j5oDxMPb1qcd8cIvpA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", "dev": true, "requires": { "bl": "^1.0.0", - "buffer-alloc": "^1.1.0", + "buffer-alloc": "^1.2.0", "end-of-stream": "^1.0.0", "fs-constants": "^1.0.0", "readable-stream": "^2.3.0", - "to-buffer": "^1.1.0", + "to-buffer": "^1.1.1", "xtend": "^4.0.0" } }, @@ -10114,11 +10152,39 @@ "dev": true, "requires": { "execa": "^0.7.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + } } }, "text-encoding": { "version": "0.6.4", - "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", + "resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=", "dev": true }, @@ -10157,7 +10223,7 @@ }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -10250,12 +10316,6 @@ "repeat-string": "^1.6.1" } }, - "to-utf8": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/to-utf8/-/to-utf8-0.0.1.tgz", - "integrity": "sha1-0Xrqcv8vujm55DYBvns/9y4ImFI=", - "dev": true - }, "toml": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/toml/-/toml-2.3.3.tgz", @@ -10269,11 +10329,12 @@ "dev": true }, "tough-cookie": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", - "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "dev": true, "requires": { + "psl": "^1.1.24", "punycode": "^1.4.1" }, "dependencies": { @@ -10300,30 +10361,12 @@ "integrity": "sha1-0EsigOTHkqWBVCnve4tgxkyczDQ=", "dev": true }, - "trim": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", - "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=", - "dev": true - }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, - "trim-trailing-lines": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz", - "integrity": "sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg==", - "dev": true - }, - "trough": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.2.tgz", - "integrity": "sha512-FHkoUZvG6Egrv9XZAyYGKEyb1JMsFphgPjoczkZC2y6W93U1jswcVURB8MUvtsahEPEVACyxD47JAL63vF4JsQ==", - "dev": true - }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -10349,8 +10392,7 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true + "dev": true }, "type-check": { "version": "0.3.2", @@ -10362,9 +10404,9 @@ } }, "type-detect": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.5.tgz", - "integrity": "sha512-N9IvkQslUGYGC24RkJk1ba99foK6TkwC2FHAEBlQFBP0RxQZS8ZpJuAZcwiY/w9ZJHFQb1aOXBI60OdxhTrwEQ==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "typedarray": { @@ -10398,9 +10440,9 @@ } }, "uglifyjs-webpack-plugin": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz", - "integrity": "sha512-1VicfKhCYHLS8m1DCApqBhoulnASsEoJ/BvpUpP4zoNAPpKzdH+ghk0olGJMmwX2/jprK2j3hAHdUbczBSy2FA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz", + "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", "dev": true, "requires": { "cacache": "^10.0.4", @@ -10431,35 +10473,11 @@ } }, "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", + "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=", "dev": true }, - "unherit": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.1.tgz", - "integrity": "sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "xtend": "^4.0.1" - } - }, - "unified": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/unified/-/unified-4.2.1.tgz", - "integrity": "sha1-dv9Dqo2kMPbn5KVchOusKtLPzS4=", - "dev": true, - "requires": { - "bail": "^1.0.0", - "extend": "^3.0.0", - "has": "^1.0.1", - "once": "^1.3.3", - "trough": "^1.0.0", - "vfile": "^1.0.0" - } - }, "union-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", @@ -10496,18 +10514,18 @@ } }, "unique-filename": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.0.tgz", - "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "dev": true, "requires": { "unique-slug": "^2.0.0" } }, "unique-slug": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.0.tgz", - "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", + "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", "dev": true, "requires": { "imurmurhash": "^0.1.4" @@ -10522,30 +10540,6 @@ "crypto-random-string": "^1.0.0" } }, - "unist-util-is": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-2.1.2.tgz", - "integrity": "sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw==", - "dev": true - }, - "unist-util-remove-position": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz", - "integrity": "sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q==", - "dev": true, - "requires": { - "unist-util-visit": "^1.1.0" - } - }, - "unist-util-visit": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.3.1.tgz", - "integrity": "sha512-0fdB9EQJU0tho5tK0VzOJzAQpPv2LyLZ030b10GxuzAWEfvd54mpY7BMjQ1L69k2YNvL+SvxRzH0yUIehOO8aA==", - "dev": true, - "requires": { - "unist-util-is": "^2.1.1" - } - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -10605,9 +10599,9 @@ "dev": true }, "upath": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.0.5.tgz", - "integrity": "sha512-qbKn90aDQ0YEwvXoLqj0oiuUYroLX2lVHZ+b+xwjozFasAOC4GneDq5+OaIG5Zj+jFmbz/uO+f7a9qxjktJQww==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", "dev": true }, "update-notifier": { @@ -10647,16 +10641,10 @@ "supports-color": "^5.3.0" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" @@ -10664,12 +10652,6 @@ } } }, - "update-section": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/update-section/-/update-section-0.3.3.tgz", - "integrity": "sha1-RY8Xgg03gg3GDiC4bZQ5GwASMVg=", - "dev": true - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -10713,21 +10695,10 @@ } }, "use": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.0.tgz", - "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", - "dev": true, - "requires": { - "kind-of": "^6.0.2" - }, - "dependencies": { - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - } - } + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true }, "user-home": { "version": "2.0.0", @@ -10760,15 +10731,15 @@ "dev": true }, "v8-compile-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz", - "integrity": "sha512-qNdTUMaCjPs4eEnM3W9H94R3sU70YCuT+/ST7nUf+id1bVOrdjrpUaeZLqPBPRph3hsgn4a4BvwpxhHZx+oSDg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz", + "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==", "dev": true }, "validate-npm-package-license": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz", - "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, "requires": { "spdx-correct": "^3.0.0", @@ -10786,18 +10757,6 @@ "extsprintf": "^1.2.0" } }, - "vfile": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-1.4.0.tgz", - "integrity": "sha1-wP1vpIT43r23cfaMMe112I2pf+c=", - "dev": true - }, - "vfile-location": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.3.tgz", - "integrity": "sha512-zM5/l4lfw1CBoPx3Jimxoc5RNDAHHpk6AM6LM0pTIkm5SUSsx8ZekZ0PVdf0WEZ7kjlhSt7ZlqbRL6Cd6dBs6A==", - "dev": true - }, "vm-browserify": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", @@ -10828,15 +10787,15 @@ } }, "web-ext": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-2.7.0.tgz", - "integrity": "sha512-hXj/MV/x6G0oxplOirV4/j7BJ5MZJ2yZHml0gulBr7mH2BMNyTJHdRi+qzVBNPFdBMLV0/PS05YGZ7xr2YmwVA==", + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-2.9.1.tgz", + "integrity": "sha512-sK5ebAiUNJFG+KfFjjvWks9ihecy0TdVCrrnSW/tZ15QFO6u4LCIQKCuBr7FyGMjC+IOGJFB7pS1ZbyPNJ72GQ==", "dev": true, "requires": { "@cliqz-oss/firefox-client": "0.3.1", "@cliqz-oss/node-firefox-connect": "1.2.1", "adbkit": "2.11.0", - "addons-linter": "1.0.0", + "addons-linter": "1.3.1", "babel-polyfill": "6.26.0", "babel-runtime": "6.26.0", "bunyan": "1.8.12", @@ -10846,11 +10805,11 @@ "es6-error": "4.1.1", "es6-promisify": "5.0.0", "event-to-promise": "0.8.0", - "firefox-profile": "1.1.0", + "firefox-profile": "1.2.0", "fx-runner": "1.0.9", "git-rev-sync": "1.9.1", - "minimatch": "3.0.4", "mkdirp": "0.5.1", + "multimatch": "2.1.0", "mz": "2.7.0", "node-notifier": "5.2.1", "opn": "5.3.0", @@ -10892,23 +10851,22 @@ "dev": true }, "webpack": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.16.0.tgz", - "integrity": "sha512-oNx9djAd6uAcccyfqN3hyXLNMjZHiRySZmBQ4c8FNmf1SNJGhx7n9TSvHNyXxgToRdH65g/Q97s94Ip9N6F7xg==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.25.1.tgz", + "integrity": "sha512-T0GU/3NRtO4tMfNzsvpdhUr8HnzA4LTdP2zd+e5zd6CdOH5vNKHnAlO+DvzccfhPdzqRrALOFcjYxx7K5DWmvA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.5.13", - "@webassemblyjs/helper-module-context": "1.5.13", - "@webassemblyjs/wasm-edit": "1.5.13", - "@webassemblyjs/wasm-opt": "1.5.13", - "@webassemblyjs/wasm-parser": "1.5.13", + "@webassemblyjs/ast": "1.7.11", + "@webassemblyjs/helper-module-context": "1.7.11", + "@webassemblyjs/wasm-edit": "1.7.11", + "@webassemblyjs/wasm-parser": "1.7.11", "acorn": "^5.6.2", "acorn-dynamic-import": "^3.0.0", "ajv": "^6.1.0", "ajv-keywords": "^3.1.0", "chrome-trace-event": "^1.0.0", "enhanced-resolve": "^4.1.0", - "eslint-scope": "^3.7.1", + "eslint-scope": "^4.0.0", "json-parse-better-errors": "^1.0.2", "loader-runner": "^2.3.0", "loader-utils": "^1.1.0", @@ -10918,55 +10876,46 @@ "neo-async": "^2.5.0", "node-libs-browser": "^2.0.0", "schema-utils": "^0.4.4", - "tapable": "^1.0.0", + "tapable": "^1.1.0", "uglifyjs-webpack-plugin": "^1.2.4", "watchpack": "^1.5.0", - "webpack-sources": "^1.0.1" + "webpack-sources": "^1.3.0" }, "dependencies": { - "ajv": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.2.tgz", - "integrity": "sha512-hOs7GfvI6tUI1LfZddH82ky6mOMyTuY0mk7kE2pWpmhhUSkumzaTO5vbVwij39MdwPQWCV4Zv57Eo06NtL/GVA==", + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "eslint-scope": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.1" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true } } }, "webpack-cli": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.0.8.tgz", - "integrity": "sha512-KnRLJ0BUaYRqrhAMb9dv3gzdmhmgIMKo0FmdsnmfqbPGtLnnZ6tORZAvmmKfr+A0VgiVpqC60Gv7Ofg0R2CHtQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.1.2.tgz", + "integrity": "sha512-Cnqo7CeqeSvC6PTdts+dywNi5CRlIPbLx1AoUPK2T6vC1YAugMG3IOoO9DmEscd+Dghw7uRlnzV1KwOe5IrtgQ==", "dev": true, "requires": { "chalk": "^2.4.1", "cross-spawn": "^6.0.5", - "enhanced-resolve": "^4.0.0", - "global-modules-path": "^2.1.0", - "import-local": "^1.0.0", - "inquirer": "^6.0.0", + "enhanced-resolve": "^4.1.0", + "global-modules-path": "^2.3.0", + "import-local": "^2.0.0", "interpret": "^1.1.0", "loader-utils": "^1.1.0", - "supports-color": "^5.4.0", - "v8-compile-cache": "^2.0.0", - "yargs": "^11.1.0" + "supports-color": "^5.5.0", + "v8-compile-cache": "^2.0.2", + "yargs": "^12.0.2" }, "dependencies": { "ansi-styles": { @@ -10989,113 +10938,84 @@ "supports-color": "^5.3.0" } }, - "chardet": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.5.0.tgz", - "integrity": "sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "locate-path": "^3.0.0" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "external-editor": { + "locate-path": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.0.tgz", - "integrity": "sha512-mpkfj0FEdxrIhOC04zk85X7StNtr0yXnG7zCb+8ikO8OJi2jsHh5YGoknNTyXgsbHOf1WOOcVU3kPFWT2WgCkQ==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "chardet": "^0.5.0", - "iconv-lite": "^0.4.22", - "tmp": "^0.0.33" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, - "inquirer": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.0.0.tgz", - "integrity": "sha512-tISQWRwtcAgrz+SHPhTH7d3e73k31gsOy6i1csonLc0u1dVK/wYvuOnFeiWqC5OXFIYbmrIFInef31wbT8MEJg==", + "p-limit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", + "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", "dev": true, "requires": { - "ansi-escapes": "^3.0.0", - "chalk": "^2.0.0", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.0", - "figures": "^2.0.0", - "lodash": "^4.3.0", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.1.0", - "string-width": "^2.1.0", - "strip-ansi": "^4.0.0", - "through": "^2.3.6" + "p-try": "^2.0.0" } }, - "rxjs": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.1.tgz", - "integrity": "sha512-OwMxHxmnmHTUpgO+V7dZChf3Tixf4ih95cmXjzzadULziVl/FKhHScGLj4goEw9weePVOH2Q0+GcCBUhKCZc/g==", + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "tslib": "^1.9.0" + "p-limit": "^2.0.0" } }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==", + "dev": true + }, "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { "has-flag": "^3.0.0" } }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, "yargs": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz", - "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz", + "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==", "dev": true, "requires": { "cliui": "^4.0.0", - "decamelize": "^1.1.1", - "find-up": "^2.1.0", + "decamelize": "^2.0.0", + "find-up": "^3.0.0", "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", + "os-locale": "^3.0.0", "require-directory": "^2.1.1", "require-main-filename": "^1.0.1", "set-blocking": "^2.0.0", "string-width": "^2.0.0", "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^9.0.2" + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^10.1.0" } } } }, "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz", + "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==", "dev": true, "requires": { "source-list-map": "^2.0.0", @@ -11111,9 +11031,9 @@ } }, "whatwg-url": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.1.tgz", - "integrity": "sha512-FwygsxsXx27x6XXuExA/ox3Ktwcbf+OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0+dR+JVtf7MlbqZjw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", "dev": true, "requires": { "lodash.sortby": "^4.7.0", @@ -11143,9 +11063,9 @@ "dev": true }, "widest-line": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.0.tgz", - "integrity": "sha1-AUKk6KJD+IgsAjOqDgKBqnYVInM=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "dev": true, "requires": { "string-width": "^2.1.1" @@ -11189,7 +11109,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { @@ -11219,7 +11139,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -11272,7 +11192,7 @@ }, "xmlbuilder": { "version": "9.0.7", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=", "dev": true }, @@ -11283,9 +11203,9 @@ "dev": true }, "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", + "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==", "dev": true }, "xtend": { @@ -11308,7 +11228,7 @@ }, "yargs": { "version": "6.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", + "resolved": "http://registry.npmjs.org/yargs/-/yargs-6.6.0.tgz", "integrity": "sha1-eC7CHvQDNF+DCoCMo9UTr1YGUgg=", "dev": true, "requires": { @@ -11350,6 +11270,12 @@ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", "dev": true }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, "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", @@ -11359,9 +11285,18 @@ "number-is-nan": "^1.0.0" } }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, "os-locale": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -11381,7 +11316,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { @@ -11402,7 +11337,7 @@ }, "yargs-parser": { "version": "4.2.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", + "resolved": "http://registry.npmjs.org/yargs-parser/-/yargs-parser-4.2.1.tgz", "integrity": "sha1-KczqwNxPA8bIe0qfIX3RjJ90hxw=", "dev": true, "requires": { @@ -11412,31 +11347,22 @@ } }, "yargs-parser": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz", - "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "dev": true, "requires": { "camelcase": "^4.1.0" } }, "yauzl": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.1.tgz", - "integrity": "sha1-qBmB6nCleUYTOIPwKcWCGok1mn8=", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.9.2.tgz", + "integrity": "sha1-T7G8euH8L1cDe1SvasyP4QMcW3c=", "dev": true, "requires": { "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.0.1" - } - }, - "zip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zip/-/zip-1.2.0.tgz", - "integrity": "sha1-rQrUImUwm+QutW/IYZThfCTmapw=", - "dev": true, - "requires": { - "bops": "~0.1.1" + "fd-slicer": "~1.1.0" } }, "zip-dir": { @@ -11451,9 +11377,18 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true + }, + "jszip": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-2.6.1.tgz", + "integrity": "sha1-uI86ey5noqBIFSmCx6N1bZxIKPA=", + "dev": true, + "requires": { + "pako": "~1.0.2" + } } } }, diff --git a/webext/package.json b/webext/package.json index 59d3f211..1449002d 100644 --- a/webext/package.json +++ b/webext/package.json @@ -10,23 +10,23 @@ ] }, "devDependencies": { - "babel-eslint": "^8.2.5", + "babel-eslint": "^8.2.6", "babel-loader": "^7.1.5", "babel-preset-es2015": "^6.24.1", "babel-register": "^6.26.0", - "chai": "^4.1.2", - "copy-webpack-plugin": "^4.5.2", - "eslint": "^5.1.0", + "chai": "^4.2.0", + "copy-webpack-plugin": "^4.6.0", + "eslint": "^5.8.0", "mocha": "^5.2.0", "prettier": "1.13.7", - "sinon": "^6.1.3", + "sinon": "^6.3.5", "strip-ansi": "^4.0.0", - "web-ext": "^2.7.0", - "webpack": "^4.16.0", - "webpack-cli": "^3.0.8" + "web-ext": "^2.9.1", + "webpack": "^4.25.1", + "webpack-cli": "^3.1.2" }, "dependencies": { - "lodash": "^4.17.10", + "lodash": "^4.17.11", "string-width": "^2.1.1" } } From 0bda8f16f5911c2f83fc19a69636d5d14e3ce58e Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 9 Nov 2018 16:43:11 +0900 Subject: [PATCH 22/77] Adds Golang clipboard dep --- interfacer/Gopkg.lock | 15 ++++++++++++--- interfacer/Gopkg.toml | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/interfacer/Gopkg.lock b/interfacer/Gopkg.lock index 1e356b04..3b87e4f1 100644 --- a/interfacer/Gopkg.lock +++ b/interfacer/Gopkg.lock @@ -9,6 +9,14 @@ revision = "2600fb119af974220d3916a5916d6e31176aac1b" version = "v1.0.1" +[[projects]] + digest = "1:1ca95505ec8a6d69762d2b5d9ab7e66ae619365b2e26031e220b675ff6fb010b" + name = "github.com/atotto/clipboard" + packages = ["."] + pruneopts = "UT" + revision = "aa9549103943c05f3e8951009cdb6a0bec2c8949" + version = "v0.1.1" + [[projects]] digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" name = "github.com/fsnotify/fsnotify" @@ -255,11 +263,11 @@ "html/charset", ] pruneopts = "UT" - revision = "10aee181995363b41f712a55844a0dd52ea04646" + revision = "03003ca0c849e57b6ea29a4bab8d3cb6e4d568fe" [[projects]] branch = "master" - digest = "1:98f84f98eb433d0c9e5d3475a6c4fceaa619c989f04880cb683d34012f76dab8" + digest = "1:8999bf106679c454ab912263f73efc15ed9ff18504572497a00ac04cc1ac2bcb" name = "golang.org/x/sys" packages = [ "unix", @@ -267,7 +275,7 @@ "windows/registry", ] pruneopts = "UT" - revision = "7155702f2d47d94b134229da97195d0130cab001" + revision = "66b7b1311ac80bbafcd2daeef9a5e6e2cd1e2399" [[projects]] digest = "1:4392fcf42d5cf0e3ff78c96b2acf8223d49e4fdc53eb77c99d2f8dfe4680e006" @@ -326,6 +334,7 @@ analyzer-version = 1 input-imports = [ "github.com/NYTimes/gziphandler", + "github.com/atotto/clipboard", "github.com/gdamore/tcell", "github.com/gdamore/tcell/terminfo", "github.com/go-errors/errors", diff --git a/interfacer/Gopkg.toml b/interfacer/Gopkg.toml index 769be61c..8c138117 100644 --- a/interfacer/Gopkg.toml +++ b/interfacer/Gopkg.toml @@ -29,6 +29,10 @@ name = "github.com/NYTimes/gziphandler" version = "1.0.1" +[[constraint]] + name = "github.com/atotto/clipboard" + version = "0.1.1" + [[constraint]] name = "github.com/gdamore/tcell" version = "1.1.0" From 6f998bea69fa268fec7b7382d94dad8aa7194df8 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 9 Nov 2018 16:43:35 +0900 Subject: [PATCH 23/77] Refactors Vim code from tty.go into its own file --- interfacer/src/browsh/tty.go | 593 +---------------------------- interfacer/src/browsh/vim_mode.go | 601 ++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+), 588 deletions(-) create mode 100644 interfacer/src/browsh/vim_mode.go diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index 098b447c..f7663b5e 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -4,110 +4,26 @@ import ( "encoding/json" "fmt" "os" - "reflect" "strconv" - "strings" "time" - "unicode" - "github.com/atotto/clipboard" "github.com/gdamore/tcell" "github.com/go-errors/errors" "github.com/spf13/viper" ) -type VimMode int - -const ( - NormalMode VimMode = iota + 1 - InsertMode - FindMode - LinkMode - LinkModeNewTab - LinkModeCopy - WaitMode - VisualMode - CaretMode - MakeMarkMode - GotoMarkMode -) - -type mark struct { - tabID int - URI string - xScroll int - yScroll int -} - type Coordinate struct { X, Y int } -type HintRect struct { - Bottom int `json:"bottom"` - Top int `json:"top"` - Left int `json:"left"` - Right int `json:"right"` - Width int `json:"width"` - Height int `json:"height"` - Href string `json:"href"` -} - var ( - screen tcell.Screen + screen tcell.Screen + // The height of the tabs and URL bar uiHeight = 2 // IsMonochromeMode decides whether to render the TTY in full colour or monochrome IsMonochromeMode = false - // vimMode - vimMode = NormalMode - vimCommandsBindings = make(map[string]string) - keyEvents = make([]*tcell.EventKey, 11) - //runeTime = make(map[rune]time.Time) - //lastRune rune - waitModeStartTime time.Time - findText string - // marks - globalMarkMap = make(map[rune]*mark) - localMarkMap = make(map[int]map[rune]*mark) - // position coordinate for caret mode - caretPos Coordinate - // for link modes - linkText string - linkHintRects []HintRect - linkHintKeys = "asdfwerxcv" - linkHints []string - linkHintsToRects = make(map[string]*HintRect) - linkModeWithHints = true - linkHintWriteStringCalls *func() ) -func init() { - setupLinkHints() -} - -func setupLinkHints() { - lowerAlpha := "abcdefghijklmnopqrstuvwxyz" - missingAlpha := lowerAlpha - - // Use linkHintKeys first to generate link hints - for i := 0; i < len(linkHintKeys); i++ { - for j := 0; j < len(linkHintKeys); j++ { - linkHints = append(linkHints, string(linkHintKeys[i])+string(linkHintKeys[j])) - } - missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) - } - - // missingAlpha contains all keys that aren't in linkHintKeys - // we use this to generate the last link hint key combinations, - // so this will only be used when we run out of linkHintKeys based - // link hint key combinations. - for i := 0; i < len(missingAlpha); i++ { - for j := 0; j < len(lowerAlpha); j++ { - linkHints = append(linkHints, string(missingAlpha[i])+string(lowerAlpha[j])) - } - } -} - func setupTcell() { var err error if err = screen.Init(); err != nil { @@ -183,6 +99,7 @@ func handleUserKeyPress(ev *tcell.EventKey) { } } +// Matches a human-readable key defintion with a Tcell event func isKey(userKey string, ev *tcell.EventKey) bool { key := viper.GetStringSlice(userKey) runeMatch := []rune(key[0])[0] == ev.Rune() @@ -230,69 +147,6 @@ func isMultiLineEnter(ev *tcell.EventKey) bool { return activeInputBox.isMultiLine() && ev.Key() == 13 && ev.Modifiers() != 4 } -// Moves the caret in CaretMode. -// testFunc is a function that tests for the reaching of the boundaries of the given axis. -// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. -// The step size and direction is given by the value of step. -func moveVimCaret(testFunc func() bool, valRef *int, step int) { - var prevCell, nextCell, nextNextCell cell - var r rune - hasNextNextCell := false - - for testFunc() { - prevCell = getCell(caretPos.X, caretPos.Y-uiHeight) - *valRef += step - nextCell = getCell(caretPos.X, caretPos.Y-uiHeight) - - if testFunc() { - *valRef += step - nextNextCell = getCell(caretPos.X, caretPos.Y-uiHeight) - *valRef -= step - hasNextNextCell = true - } else { - hasNextNextCell = false - } - - r = nextCell.character[0] - // Check if the next cell is different in any way - if !reflect.DeepEqual(prevCell, nextCell) { - if hasNextNextCell { - // This condition should apply to the spaces between words and the like - // Checking with unicode.isSpace() didn't give correct results for some reason - // TODO: find out what that reason is and improve this - if !unicode.IsLetter(r) && unicode.IsLetter(nextNextCell.character[0]) { - continue - } - // If the upcoming cell is deeply equal we can continue to go forward - if reflect.DeepEqual(nextCell, nextNextCell) { - continue - } - } - // This cell is different and other conditions for continuing don't apply - // therefore we stop going forward. - break - } - } -} - -// TODO: This fails if the tab with mark.tabID doesn't exist anymore it should recreate said tab, then go to the mark's URL and position -func gotoMark(mark *mark) { - if CurrentTab.ID != mark.tabID { - ensureTabExists(mark.tabID) - switchToTab(mark.tabID) - } - if CurrentTab.URI != mark.URI { - sendMessageToWebExtension("/tab_command,/url," + mark.URI) - //sleep? - } - doScrollAbsolute(mark.xScroll, mark.yScroll) -} - -// Make a mark at the current position in the current tab -func makeMark() *mark { - return &mark{CurrentTab.ID, CurrentTab.URI, CurrentTab.frame.xScroll, CurrentTab.frame.yScroll} -} - func generateLeftClickYHack(x, y int, yHack bool) { newMouseEvent := tcell.NewEventMouse(x, y+uiHeight, tcell.Button1, 0) handleMouseEventYHack(newMouseEvent, yHack) @@ -314,358 +168,6 @@ func generateMiddleClick(x, y int) { handleMouseEvent(newMouseEvent) } -func goIntoWaitMode() { - vimMode = WaitMode - waitModeStartTime = time.Now() -} - -func updateLinkHintDisplay() { - linkHintsToRects = make(map[string]*HintRect) - lh := len(linkHintRects) - var ht string - // List of closures - var fc []*func() - - for i, r := range linkHintRects { - // When the number of link hints is small enough - // using just one key for individual link hints suffices. - // Otherwise use the prepared link hint key combinations. - if lh <= len(linkHintKeys) { - ht = string(linkHintKeys[i]) - } else { - ht = linkHints[i] - } - // Add the key combination ht to the linkHintsToRects map. - // When the user presses it, we can easily lookup the - // link hint properties associated with it. - linkHintsToRects[ht] = &linkHintRects[i] - - // When the first key got hit, - // shorten the link hints accordingly - offsetLeft := 0 - if strings.HasPrefix(ht, linkText) { - ht = ht[len(linkText):len(ht)] - offsetLeft = len(linkText) - } - - // Make copies of parameter values - rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht - - // Link hints are in upper case in new tab mode - if vimMode == LinkModeNewTab { - htCopy = strings.ToUpper(htCopy) - } - - // Create closure - f := func() { - writeString(rLeftCopy+offsetLeft, rTopCopy+uiHeight, htCopy, tcell.StyleDefault) - } - fc = append(fc, &f) - } - // Create closure that calls the other closures - ff := func() { - for _, f := range fc { - (*f)() - } - } - linkHintWriteStringCalls = &ff -} - -func eraseLinkHints() { - linkText = "" - linkHintWriteStringCalls = nil - linkHintsToRects = make(map[string]*HintRect) - linkHintRects = nil -} - -func isNormalModeKey(ev *tcell.EventKey) bool { - if ev.Key() == tcell.KeyESC { - return true - } - return false -} - -func handleVimControl(ev *tcell.EventKey) { - var lastRune rune - command := "" - - if len(keyEvents) > 0 && keyEvents[0] != nil { - lastRune = keyEvents[len(keyEvents)-1].Rune() - } - - keyEvents = append(keyEvents, ev) - if len(keyEvents) > 10 { - keyEvents = keyEvents[1:] - } - - keyCombination := string(lastRune) + string(ev.Rune()) - - switch vimMode { - case WaitMode: - if time.Since(waitModeStartTime) < time.Millisecond*1000 { - return - } - vimMode = NormalMode - fallthrough - case NormalMode: - command = vimCommandsBindings[keyCombination] - if len(command) == 0 { - keyCombination := string(ev.Rune()) - command = vimCommandsBindings[keyCombination] - } - case InsertMode: - if isNormalModeKey(ev) { - command = "normalMode" - } - case VisualMode: - if isNormalModeKey(ev) { - command = "normalMode" - } else { - if ev.Rune() == 'c' { - command = "caretMode" - } - if ev.Rune() == 'o' { - //swap cursor position begin->end or end->begin - } - if ev.Rune() == 'y' { - //clipboard - } - } - case CaretMode: - if isNormalModeKey(ev) { - command = "normalMode" - } else { - switch ev.Key() { - case tcell.KeyEnter: - generateLeftClick(caretPos.X, caretPos.Y-uiHeight) - } - switch ev.Rune() { - case 'v': - command = "visualMode" - case 'h': - moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) - case 'l': - width, _ := screen.Size() - moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) - case 'k': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) - if caretPos.Y < uiHeight { - command = "scrollHalfPageUp" - if CurrentTab.frame.yScroll == 0 { - caretPos.Y = uiHeight - } else { - caretPos.Y += (height - uiHeight) / 2 - } - } - case 'j': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) - if caretPos.Y > height-uiHeight { - command = "scrollHalfPageDown" - caretPos.Y -= (height - uiHeight) / 2 - } - } - } - case MakeMarkMode: - if unicode.IsLower(ev.Rune()) { - if localMarkMap[CurrentTab.ID] == nil { - localMarkMap[CurrentTab.ID] = make(map[rune]*mark) - } - localMarkMap[CurrentTab.ID][ev.Rune()] = makeMark() - } else if unicode.IsUpper(ev.Rune()) { - globalMarkMap[ev.Rune()] = makeMark() - } - - command = "normalMode" - case GotoMarkMode: - if mark, ok := globalMarkMap[ev.Rune()]; ok { - gotoMark(mark) - } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { - if mark, ok := m[ev.Rune()]; ok { - gotoMark(mark) - } - } - - command = "normalMode" - case FindMode: - if isNormalModeKey(ev) { - command = "normalMode" - findText = "" - } else { - if ev.Key() == tcell.KeyEnter { - vimMode = NormalMode - command = "findText" - break - } - if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 { - if len(findText) > 0 { - findText = findText[:len(findText)-1] - } - } else { - findText += string(ev.Rune()) - } - } - case LinkMode, LinkModeNewTab, LinkModeCopy: - if isNormalModeKey(ev) { - command = "normalMode" - eraseLinkHints() - } else { - linkText += string(ev.Rune()) - updateLinkHintDisplay() - if linkModeWithHints { - if r, ok := linkHintsToRects[linkText]; ok { - if r != nil { - switch vimMode { - case LinkMode: - if (*r).Height == 2 { - generateLeftClickYHack((*r).Left, (*r).Top, true) - } else { - generateLeftClick((*r).Left, (*r).Top) - } - case LinkModeNewTab: - //generateMiddleClick(r.Left, r.Top) - sendMessageToWebExtension("/new_tab," + r.Href) - case LinkModeCopy: - clipboard.WriteAll(r.Href) - } - goIntoWaitMode() - eraseLinkHints() - } - } - } else { - coords := findAndHighlightTextOnScreen(linkText) - if len(coords) == 1 { - goIntoWaitMode() - - if vimMode == LinkModeNewTab { - generateMiddleClick(coords[0].X, coords[0].Y) - } else { - generateLeftClick(coords[0].X, coords[0].Y) - } - linkText = "" - return - } else if len(coords) == 0 { - vimMode = NormalMode - linkText = "" - return - } - } - } - } - - if len(command) > 0 { - executeVimCommand(command) - } -} - -func executeVimCommand(command string) { - switch command { - case "urlUp": - sendMessageToWebExtension("/tab_command,/url_up") - case "urlRoot": - sendMessageToWebExtension("/tab_command,/url_root") - case "scrollToTop": - doScroll(0, -CurrentTab.frame.domRowCount()) - case "scrollToBottom": - doScroll(0, CurrentTab.frame.domRowCount()) - case "scrollUp": - doScroll(0, -1) - case "scrollDown": - doScroll(0, 1) - case "scrollLeft": - doScroll(-1, 0) - case "scrollRight": - doScroll(1, 0) - case "editURL": - urlBarFocusToggle() - case "firstTab": - switchToTab(tabsOrder[0]) - case "lastTab": - switchToTab(tabsOrder[len(tabsOrder)-1]) - case "scrollHalfPageDown": - _, height := screen.Size() - doScroll(0, (height-uiHeight)/2) - case "scrollHalfPageUp": - _, height := screen.Size() - doScroll(0, -((height - uiHeight) / 2)) - case "historyBack": - sendMessageToWebExtension("/tab_command,/history_back") - case "historyForward": - sendMessageToWebExtension("/tab_command,/history_forward") - case "reload": - sendMessageToWebExtension("/tab_command,/reload") - case "prevTab": - prevTab() - case "nextTab": - nextTab() - case "previouslyVisitedTab": - previouslyVisitedTab() - case "newTab": - createNewEmptyTab() - case "removeTab": - removeTab(CurrentTab.ID) - case "restoreTab": - restoreTab() - case "duplicateTab": - duplicateTab(CurrentTab.ID) - case "moveTabLeft": - moveTabLeft(CurrentTab.ID) - case "moveTabRight": - moveTabRight(CurrentTab.ID) - case "copyURL": - clipboard.WriteAll(CurrentTab.URI) - case "openClipboardURL": - URI, _ := clipboard.ReadAll() - sendMessageToWebExtension("/tab_command,/url," + URI) - case "openClipboardURLInNewTab": - URI, _ := clipboard.ReadAll() - sendMessageToWebExtension("/new_tab," + URI) - case "focusFirstTextInput": - sendMessageToWebExtension("/tab_command,/focus_first_text_input") - case "followLinkLabeledNext": - sendMessageToWebExtension("/tab_command,/follow_link_labeled_next") - case "followLinkLabeledPrevious": - sendMessageToWebExtension("/tab_command,/follow_link_labeled_previous") - case "viewHelp": - sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") - case "openLinkInCurrentTab": - vimMode = LinkMode - sendMessageToWebExtension("/tab_command,/get_clickable_hints") - eraseLinkHints() - case "openLinkInNewTab": - vimMode = LinkModeNewTab - sendMessageToWebExtension("/tab_command,/get_link_hints") - eraseLinkHints() - case "copyLinkURL": - vimMode = LinkModeCopy - sendMessageToWebExtension("/tab_command,/get_link_hints") - eraseLinkHints() - case "findText": - fallthrough - case "findNext": - sendMessageToWebExtension("/tab_command,/find_next," + findText) - case "findPrevious": - sendMessageToWebExtension("/tab_command,/find_previous," + findText) - case "makeMark": - vimMode = MakeMarkMode - case "gotoMark": - vimMode = GotoMarkMode - case "insertMode": - vimMode = InsertMode - case "findMode": - vimMode = FindMode - case "normalMode": - vimMode = NormalMode - case "visualMode": - vimMode = VisualMode - case "caretMode": - vimMode = CaretMode - width, height := screen.Size() - caretPos.X, caretPos.Y = width/2, height/2 - } -} - func doScroll(relX int, relY int) { doScrollAbsolute(CurrentTab.frame.xScroll+relX, CurrentTab.frame.yScroll+relY) } @@ -795,93 +297,6 @@ func renderCurrentTabWindow() { screen.Show() } -func searchScreenForText(text string) []Coordinate { - var offsets = make([]Coordinate, 0) - var splitString []string - var r rune - var s string - width, height := screen.Size() - screenText := "" - index := 0 - - for y := 0; y < height-uiHeight; y++ { - screenText = "" - for x := 0; x < width; x++ { - r = getCell(x, y).character[0] - s = string(r) - if len(s) == 0 || len(s) > 1 { - screenText += " " - } else { - screenText += string(getCell(x, y).character[0]) - } - } - index = 0 - splitString = strings.Split(strings.ToLower(screenText), strings.ToLower(text)) - for _, s := range splitString { - if index+len(s) >= width { - break - } - - offsets = append(offsets, Coordinate{index + len(s), y}) - index += len(s) + len(text) - } - } - return offsets -} - -func findAndHighlightTextOnScreen(text string) []Coordinate { - var x, y int - var styling = tcell.StyleDefault - - offsets := searchScreenForText(text) - for _, offset := range offsets { - y = offset.Y - x = offset.X - for z := 0; z < len(text); z++ { - screen.SetContent(x+z, y+uiHeight, rune(text[z]), nil, styling) - } - } - screen.Show() - return offsets -} - -// Parse incoming link hints -func parseJSONLinkHints(jsonString string) { - jsonBytes := []byte(jsonString) - if err := json.Unmarshal(jsonBytes, &linkHintRects); err != nil { - Shutdown(err) - } - - // Optimize link hint positions - for i := 0; i < len(linkHintRects); i++ { - r := &linkHintRects[i] - - // For links that are more than one line high - // we want to position the link hint in the vertical middle - if r.Height > 2 { - if r.Height%2 == 0 { - r.Top += r.Height / 2 - } else { - r.Top += r.Height/2 - 1 - } - } - - // For links that are more one character long we try to move - // the link hint two characters to the right, if possible. - if r.Width > 1 { - o := r.Left - r.Left += r.Width/2 - 1 - if r.Left > o+2 { - r.Left = o + 2 - } - } - } - - Log("Received parseJSONLinkHint") - // This is where the display of actual link hints is prepared - updateLinkHintDisplay() -} - func getCell(x, y int) cell { var currentCell cell var ok bool @@ -898,6 +313,8 @@ func getCell(x, y int) cell { return currentCell } +// These are the dark and light grey squares that appear in the background to indicate that +// nothing has been rendered there yet. func getHatchedCellColours(x int) (tcell.Color, tcell.Color) { var bgColour, fgColour tcell.Color if x%2 == 0 { diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go new file mode 100644 index 00000000..cd3ad673 --- /dev/null +++ b/interfacer/src/browsh/vim_mode.go @@ -0,0 +1,601 @@ +package browsh + +import ( + "encoding/json" + "reflect" + "strings" + "time" + "unicode" + + "github.com/atotto/clipboard" + "github.com/gdamore/tcell" +) + +// TODO: Capitalised variables mean that developers can expect them to be publically availably +// as part of the API of the package. I don't think that is the intended case here. +type VimMode int + +const ( + NormalMode VimMode = iota + 1 + InsertMode + FindMode + LinkMode + LinkModeNewTab + LinkModeCopy + WaitMode + VisualMode + CaretMode + MakeMarkMode + GotoMarkMode +) + +// TODO: What's a mark? +type mark struct { + tabID int + URI string + xScroll int + yScroll int +} + +type HintRect struct { + Bottom int `json:"bottom"` + Top int `json:"top"` + Left int `json:"left"` + Right int `json:"right"` + Width int `json:"width"` + Height int `json:"height"` + Href string `json:"href"` +} + +var ( + // vimMode + vimMode = NormalMode + vimCommandsBindings = make(map[string]string) + keyEvents = make([]*tcell.EventKey, 11) + //runeTime = make(map[rune]time.Time) + //lastRune rune + waitModeStartTime time.Time + findText string + // marks + globalMarkMap = make(map[rune]*mark) + localMarkMap = make(map[int]map[rune]*mark) + // position coordinate for caret mode + caretPos Coordinate + // for link modes + linkText string + linkHintRects []HintRect + linkHintKeys = "asdfwerxcv" + linkHints []string + linkHintsToRects = make(map[string]*HintRect) + linkModeWithHints = true + linkHintWriteStringCalls *func() +) + +func init() { + setupLinkHints() +} + +func setupLinkHints() { + lowerAlpha := "abcdefghijklmnopqrstuvwxyz" + missingAlpha := lowerAlpha + + // Use linkHintKeys first to generate link hints + for i := 0; i < len(linkHintKeys); i++ { + for j := 0; j < len(linkHintKeys); j++ { + linkHints = append(linkHints, string(linkHintKeys[i])+string(linkHintKeys[j])) + } + missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) + } + + // missingAlpha contains all keys that aren't in linkHintKeys + // we use this to generate the last link hint key combinations, + // so this will only be used when we run out of linkHintKeys based + // link hint key combinations. + for i := 0; i < len(missingAlpha); i++ { + for j := 0; j < len(lowerAlpha); j++ { + linkHints = append(linkHints, string(missingAlpha[i])+string(lowerAlpha[j])) + } + } +} + +// Moves the caret in CaretMode. +// isCaretAtBoundary is a function that tests for the reaching of the boundaries of the given axis. +// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. +// The step size and direction is given by the value of step. +func moveVimCaret(isCaretAtBoundary func() bool, valRef *int, step int) { + var prevCell, nextCell, nextNextCell cell + var r rune + hasNextNextCell := false + + for isCaretAtBoundary() { + prevCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef += step + nextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + + if isCaretAtBoundary() { + *valRef += step + nextNextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef -= step + hasNextNextCell = true + } else { + hasNextNextCell = false + } + + r = nextCell.character[0] + // Check if the next cell is different in any way + if !reflect.DeepEqual(prevCell, nextCell) { + if hasNextNextCell { + // This condition should apply to the spaces between words and the like + // Checking with unicode.isSpace() didn't give correct results for some reason + // TODO: find out what that reason is and improve this + if !unicode.IsLetter(r) && unicode.IsLetter(nextNextCell.character[0]) { + continue + } + // If the upcoming cell is deeply equal we can continue to go forward + if reflect.DeepEqual(nextCell, nextNextCell) { + continue + } + } + // This cell is different and other conditions for continuing don't apply + // therefore we stop going forward. + break + } + } +} + +// TODO: This fails if the tab with mark.tabID doesn't exist anymore it should recreate said tab, then go to the mark's URL and position +func gotoMark(mark *mark) { + if CurrentTab.ID != mark.tabID { + ensureTabExists(mark.tabID) + switchToTab(mark.tabID) + } + if CurrentTab.URI != mark.URI { + sendMessageToWebExtension("/tab_command,/url," + mark.URI) + //sleep? + } + doScrollAbsolute(mark.xScroll, mark.yScroll) +} + +// Make a mark at the current position in the current tab +func makeMark() *mark { + return &mark{CurrentTab.ID, CurrentTab.URI, CurrentTab.frame.xScroll, CurrentTab.frame.yScroll} +} + +func goIntoWaitMode() { + vimMode = WaitMode + waitModeStartTime = time.Now() +} + +func updateLinkHintDisplay() { + linkHintsToRects = make(map[string]*HintRect) + lh := len(linkHintRects) + var ht string + // List of closures + var fc []*func() + + for i, r := range linkHintRects { + // When the number of link hints is small enough + // using just one key for individual link hints suffices. + // Otherwise use the prepared link hint key combinations. + if lh <= len(linkHintKeys) { + ht = string(linkHintKeys[i]) + } else { + ht = linkHints[i] + } + // Add the key combination ht to the linkHintsToRects map. + // When the user presses it, we can easily lookup the + // link hint properties associated with it. + linkHintsToRects[ht] = &linkHintRects[i] + + // When the first key got hit, + // shorten the link hints accordingly + offsetLeft := 0 + if strings.HasPrefix(ht, linkText) { + ht = ht[len(linkText):len(ht)] + offsetLeft = len(linkText) + } + + // Make copies of parameter values + rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht + + // Link hints are in upper case in new tab mode + if vimMode == LinkModeNewTab { + htCopy = strings.ToUpper(htCopy) + } + + // Create closure + f := func() { + writeString(rLeftCopy+offsetLeft, rTopCopy+uiHeight, htCopy, tcell.StyleDefault) + } + fc = append(fc, &f) + } + // Create closure that calls the other closures + ff := func() { + for _, f := range fc { + (*f)() + } + } + linkHintWriteStringCalls = &ff +} + +func eraseLinkHints() { + linkText = "" + linkHintWriteStringCalls = nil + linkHintsToRects = make(map[string]*HintRect) + linkHintRects = nil +} + +func isNormalModeKey(ev *tcell.EventKey) bool { + if ev.Key() == tcell.KeyESC { + return true + } + return false +} + +func handleVimControl(ev *tcell.EventKey) { + var lastRune rune + command := "" + + if len(keyEvents) > 0 && keyEvents[0] != nil { + lastRune = keyEvents[len(keyEvents)-1].Rune() + } + + keyEvents = append(keyEvents, ev) + if len(keyEvents) > 10 { + keyEvents = keyEvents[1:] + } + + keyCombination := string(lastRune) + string(ev.Rune()) + + switch vimMode { + case WaitMode: + if time.Since(waitModeStartTime) < time.Millisecond*1000 { + return + } + vimMode = NormalMode + fallthrough + case NormalMode: + command = vimCommandsBindings[keyCombination] + if len(command) == 0 { + keyCombination := string(ev.Rune()) + command = vimCommandsBindings[keyCombination] + } + case InsertMode: + if isNormalModeKey(ev) { + command = "normalMode" + } + case VisualMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + if ev.Rune() == 'c' { + command = "caretMode" + } + if ev.Rune() == 'o' { + //swap cursor position begin->end or end->begin + } + if ev.Rune() == 'y' { + //clipboard + } + } + case CaretMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + switch ev.Key() { + case tcell.KeyEnter: + generateLeftClick(caretPos.X, caretPos.Y-uiHeight) + } + switch ev.Rune() { + case 'v': + command = "visualMode" + case 'h': + moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) + case 'l': + width, _ := screen.Size() + moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) + case 'k': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) + if caretPos.Y < uiHeight { + command = "scrollHalfPageUp" + if CurrentTab.frame.yScroll == 0 { + caretPos.Y = uiHeight + } else { + caretPos.Y += (height - uiHeight) / 2 + } + } + case 'j': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) + if caretPos.Y > height-uiHeight { + command = "scrollHalfPageDown" + caretPos.Y -= (height - uiHeight) / 2 + } + } + } + case MakeMarkMode: + if unicode.IsLower(ev.Rune()) { + if localMarkMap[CurrentTab.ID] == nil { + localMarkMap[CurrentTab.ID] = make(map[rune]*mark) + } + localMarkMap[CurrentTab.ID][ev.Rune()] = makeMark() + } else if unicode.IsUpper(ev.Rune()) { + globalMarkMap[ev.Rune()] = makeMark() + } + + command = "normalMode" + case GotoMarkMode: + if mark, ok := globalMarkMap[ev.Rune()]; ok { + gotoMark(mark) + } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { + if mark, ok := m[ev.Rune()]; ok { + gotoMark(mark) + } + } + + command = "normalMode" + case FindMode: + if isNormalModeKey(ev) { + command = "normalMode" + findText = "" + } else { + if ev.Key() == tcell.KeyEnter { + vimMode = NormalMode + command = "findText" + break + } + if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 { + if len(findText) > 0 { + findText = findText[:len(findText)-1] + } + } else { + findText += string(ev.Rune()) + } + } + case LinkMode, LinkModeNewTab, LinkModeCopy: + if isNormalModeKey(ev) { + command = "normalMode" + eraseLinkHints() + } else { + linkText += string(ev.Rune()) + updateLinkHintDisplay() + if linkModeWithHints { + if r, ok := linkHintsToRects[linkText]; ok { + if r != nil { + switch vimMode { + case LinkMode: + if (*r).Height == 2 { + generateLeftClickYHack((*r).Left, (*r).Top, true) + } else { + generateLeftClick((*r).Left, (*r).Top) + } + case LinkModeNewTab: + //generateMiddleClick(r.Left, r.Top) + sendMessageToWebExtension("/new_tab," + r.Href) + case LinkModeCopy: + clipboard.WriteAll(r.Href) + } + goIntoWaitMode() + eraseLinkHints() + } + } + } else { + coords := findAndHighlightTextOnScreen(linkText) + if len(coords) == 1 { + goIntoWaitMode() + + if vimMode == LinkModeNewTab { + generateMiddleClick(coords[0].X, coords[0].Y) + } else { + generateLeftClick(coords[0].X, coords[0].Y) + } + linkText = "" + return + } else if len(coords) == 0 { + vimMode = NormalMode + linkText = "" + return + } + } + } + } + + if len(command) > 0 { + executeVimCommand(command) + } +} + +func executeVimCommand(command string) { + switch command { + case "urlUp": + sendMessageToWebExtension("/tab_command,/url_up") + case "urlRoot": + sendMessageToWebExtension("/tab_command,/url_root") + case "scrollToTop": + doScroll(0, -CurrentTab.frame.domRowCount()) + case "scrollToBottom": + doScroll(0, CurrentTab.frame.domRowCount()) + case "scrollUp": + doScroll(0, -1) + case "scrollDown": + doScroll(0, 1) + case "scrollLeft": + doScroll(-1, 0) + case "scrollRight": + doScroll(1, 0) + case "editURL": + urlBarFocusToggle() + case "firstTab": + switchToTab(tabsOrder[0]) + case "lastTab": + switchToTab(tabsOrder[len(tabsOrder)-1]) + case "scrollHalfPageDown": + _, height := screen.Size() + doScroll(0, (height-uiHeight)/2) + case "scrollHalfPageUp": + _, height := screen.Size() + doScroll(0, -((height - uiHeight) / 2)) + case "historyBack": + sendMessageToWebExtension("/tab_command,/history_back") + case "historyForward": + sendMessageToWebExtension("/tab_command,/history_forward") + case "reload": + sendMessageToWebExtension("/tab_command,/reload") + case "prevTab": + prevTab() + case "nextTab": + nextTab() + case "previouslyVisitedTab": + previouslyVisitedTab() + case "newTab": + createNewEmptyTab() + case "removeTab": + removeTab(CurrentTab.ID) + case "restoreTab": + restoreTab() + case "duplicateTab": + duplicateTab(CurrentTab.ID) + case "moveTabLeft": + moveTabLeft(CurrentTab.ID) + case "moveTabRight": + moveTabRight(CurrentTab.ID) + case "copyURL": + clipboard.WriteAll(CurrentTab.URI) + case "openClipboardURL": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/tab_command,/url," + URI) + case "openClipboardURLInNewTab": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/new_tab," + URI) + case "focusFirstTextInput": + sendMessageToWebExtension("/tab_command,/focus_first_text_input") + case "followLinkLabeledNext": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_next") + case "followLinkLabeledPrevious": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_previous") + case "viewHelp": + sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") + case "openLinkInCurrentTab": + vimMode = LinkMode + sendMessageToWebExtension("/tab_command,/get_clickable_hints") + eraseLinkHints() + case "openLinkInNewTab": + vimMode = LinkModeNewTab + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "copyLinkURL": + vimMode = LinkModeCopy + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "findText": + fallthrough + case "findNext": + sendMessageToWebExtension("/tab_command,/find_next," + findText) + case "findPrevious": + sendMessageToWebExtension("/tab_command,/find_previous," + findText) + case "makeMark": + vimMode = MakeMarkMode + case "gotoMark": + vimMode = GotoMarkMode + case "insertMode": + vimMode = InsertMode + case "findMode": + vimMode = FindMode + case "normalMode": + vimMode = NormalMode + case "visualMode": + vimMode = VisualMode + case "caretMode": + vimMode = CaretMode + width, height := screen.Size() + caretPos.X, caretPos.Y = width/2, height/2 + } +} + +func searchScreenForText(text string) []Coordinate { + var offsets = make([]Coordinate, 0) + var splitString []string + var r rune + var s string + width, height := screen.Size() + screenText := "" + index := 0 + + for y := 0; y < height-uiHeight; y++ { + screenText = "" + for x := 0; x < width; x++ { + r = getCell(x, y).character[0] + s = string(r) + if len(s) == 0 || len(s) > 1 { + screenText += " " + } else { + screenText += string(getCell(x, y).character[0]) + } + } + index = 0 + splitString = strings.Split(strings.ToLower(screenText), strings.ToLower(text)) + for _, s := range splitString { + if index+len(s) >= width { + break + } + + offsets = append(offsets, Coordinate{index + len(s), y}) + index += len(s) + len(text) + } + } + return offsets +} + +func findAndHighlightTextOnScreen(text string) []Coordinate { + var x, y int + var styling = tcell.StyleDefault + + offsets := searchScreenForText(text) + for _, offset := range offsets { + y = offset.Y + x = offset.X + for z := 0; z < len(text); z++ { + screen.SetContent(x+z, y+uiHeight, rune(text[z]), nil, styling) + } + } + screen.Show() + return offsets +} + +// Parse incoming link hints +func parseJSONLinkHints(jsonString string) { + jsonBytes := []byte(jsonString) + if err := json.Unmarshal(jsonBytes, &linkHintRects); err != nil { + Shutdown(err) + } + + // Optimize link hint positions + for i := 0; i < len(linkHintRects); i++ { + r := &linkHintRects[i] + + // For links that are more than one line high + // we want to position the link hint in the vertical middle + if r.Height > 2 { + if r.Height%2 == 0 { + r.Top += r.Height / 2 + } else { + r.Top += r.Height/2 - 1 + } + } + + // For links that are more one character long we try to move + // the link hint two characters to the right, if possible. + if r.Width > 1 { + o := r.Left + r.Left += r.Width/2 - 1 + if r.Left > o+2 { + r.Left = o + 2 + } + } + } + + Log("Received parseJSONLinkHint") + // This is where the display of actual link hints is prepared + updateLinkHintDisplay() +} From e3568cd9494567c233d56f5d473bc6fd691e441c Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 9 Nov 2018 17:17:35 +0900 Subject: [PATCH 24/77] Adds some Vim-specific integration tests --- interfacer/src/browsh/frame_builder.go | 2 +- interfacer/src/browsh/input_box.go | 2 +- interfacer/src/browsh/tab.go | 2 +- interfacer/src/browsh/ui.go | 6 +-- interfacer/test/tty/setup.go | 3 +- interfacer/test/tty/tty_test.go | 2 +- interfacer/test/tty/vim_test.go | 51 ++++++++++++++++++++++++++ 7 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 interfacer/test/tty/vim_test.go diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index 37f90eed..e85e8693 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -312,7 +312,7 @@ func (f *frame) maybeFocusInputBox(x, y int) { left := inputBox.X right := inputBox.X + inputBox.Width if x >= left && x < right && y >= top && y < bottom { - urlBarFocus(false) + UrlBarFocus(false) inputBox.isActive = true activeInputBox = inputBox } diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index 405096e4..431297c8 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -181,7 +181,7 @@ func (i *inputBox) handleEnterKey(modifier tcell.ModMask) { } else { sendMessageToWebExtension("/url_bar," + string(i.text)) } - urlBarFocus(false) + UrlBarFocus(false) } if i.isMultiLine() && modifier != tcell.ModAlt { i.cursorInsertRune([]rune("\n")[0]) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index 04953451..44893139 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -134,7 +134,7 @@ func createNewEmptyTab() { CurrentTab = tab CurrentTab.frame.resetCells() renderUI() - urlBarFocus(true) + UrlBarFocus(true) renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 76de8471..afc59e09 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -87,13 +87,13 @@ func renderURLBar() { func urlBarFocusToggle() { if urlInputBox.isActive { - urlBarFocus(false) + UrlBarFocus(false) } else { - urlBarFocus(true) + UrlBarFocus(true) } } -func urlBarFocus(on bool) { +func UrlBarFocus(on bool) { if !on { activeInputBox = nil urlInputBox.isActive = false diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 71ec0797..d4963bf9 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -12,6 +12,7 @@ import ( gomega "github.com/onsi/gomega" "browsh/interfacer/src/browsh" + "github.com/spf13/viper" ) @@ -125,7 +126,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { - SpecialKey(tcell.KeyCtrlL) + browsh.UrlBarFocus(true) Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index 8c620c40..417b860c 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -8,7 +8,7 @@ import ( . "github.com/onsi/gomega" ) -func TestIntegration(t *testing.T) { +func TestMain(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Integration tests") } diff --git a/interfacer/test/tty/vim_test.go b/interfacer/test/tty/vim_test.go new file mode 100644 index 00000000..3487a023 --- /dev/null +++ b/interfacer/test/tty/vim_test.go @@ -0,0 +1,51 @@ +package test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestVim(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Integration tests") +} + +var _ = Describe("Vim tests", func() { + BeforeEach(func() { + GotoURL(testSiteURL + "/smorgasbord/") + }) + + It("should navigate to a new page by using a link hint", func() { + Expect("Another▄page").To(BeInFrameAt(12, 18)) + Keyboard("f") + Keyboard("a") + Expect("Another").To(BeInFrameAt(0, 0)) + }) + + It("should scroll the page by one line", func() { + Expect("[ˈsmœrɡɔsˌbuːɖ])▄is▄a").To(BeInFrameAt(12, 11)) + Keyboard("j") + Expect("type▄of▄Scandinavian▄").To(BeInFrameAt(12, 11)) + }) + + Describe("Tabs", func() { + BeforeEach(func() { + ensureOnlyOneTab() + }) + + It("should create a new tab", func() { + Keyboard("t") + Expect("New Tab").To(BeInFrameAt(21, 0)) + }) + + It("should cycle to the next tab", func() { + Keyboard("t") + GotoURL(testSiteURL + "/smorgasbord/another.html") + Keyboard("gt") + URL := testSiteURL + "/smorgasbord/ " + Expect(URL).To(BeInFrameAt(0, 1)) + }) + }) +}) From 3beeb76668f1887b9cb46854d307a7a8476146af Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Sun, 11 Nov 2018 23:15:35 +0900 Subject: [PATCH 25/77] Fixes tests for Vim mode Vim mode still needs a lot more tests --- interfacer/src/browsh/vim_mode.go | 4 ++++ interfacer/test/tty/setup.go | 5 ++++- interfacer/test/tty/tty_test.go | 33 ++++++++++++++----------------- interfacer/test/tty/vim_test.go | 3 ++- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index cd3ad673..0ec04825 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -11,6 +11,9 @@ import ( "github.com/gdamore/tcell" ) +// TODO: A little description as to the respective responsibilties of this code versus the +// vimium.js code. + // TODO: Capitalised variables mean that developers can expect them to be publically availably // as part of the API of the package. I don't think that is the intended case here. type VimMode int @@ -71,6 +74,7 @@ var ( linkHintWriteStringCalls *func() ) +// TODO: What's this for? func init() { setupLinkHints() } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index d4963bf9..78ae095b 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -130,6 +130,9 @@ func GotoURL(url string) { Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() + // Hack to force text to be rerendered. Because there's a bug where text sometimes doesn't get + // rendered. + mouseClick(3, 3) // TODO: Looking for the URL isn't optimal because it could be the same URL // as the previous test. gomega.Expect(url).To(BeInFrameAt(0, 1)) @@ -189,7 +192,7 @@ func GetBgColour(x, y int) [3]int32 { } func ensureOnlyOneTab() { - if len(browsh.Tabs) > 1 { + for len(browsh.Tabs) > 1 { SpecialKey(tcell.KeyCtrlW) } } diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index 417b860c..b1380472 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -1,6 +1,7 @@ package test import ( + "browsh/interfacer/src/browsh" "testing" "github.com/gdamore/tcell" @@ -13,7 +14,7 @@ func TestMain(t *testing.T) { RunSpecs(t, "Integration tests") } -var _ = Describe("Showing a basic webpage", func() { +var _ = Describe("Core functionality", func() { BeforeEach(func() { GotoURL(testSiteURL + "/smorgasbord/") }) @@ -106,10 +107,10 @@ var _ = Describe("Showing a basic webpage", func() { It("should enter multiple lines of text", func() { Keyboard(`So here is a lot of text that will hopefully split across lines`) - Expect("So here is a lot of").To(BeInFrameAt(1, 3)) - Expect("text that will").To(BeInFrameAt(1, 4)) - Expect("hopefully split across").To(BeInFrameAt(1, 5)) - Expect("lines").To(BeInFrameAt(1, 6)) + Expect("So here is a lot of").To(BeInFrameAt(1, 2)) + Expect("text that will").To(BeInFrameAt(1, 3)) + Expect("hopefully split across").To(BeInFrameAt(1, 4)) + Expect("lines").To(BeInFrameAt(1, 5)) }) It("should scroll multiple lines of text", func() { @@ -120,37 +121,33 @@ var _ = Describe("Showing a basic webpage", func() { for i := 1; i <= 6; i++ { SpecialKey(tcell.KeyUp) } - Expect("lines").To(BeInFrameAt(1, 6)) + Expect("lines").To(BeInFrameAt(1, 5)) }) }) }) Describe("Tabs", func() { BeforeEach(func() { - SpecialKey(tcell.KeyCtrlT) - }) - - AfterEach(func() { ensureOnlyOneTab() }) It("should create a new tab", func() { - Expect("New Tab").To(BeInFrameAt(21, 0)) + SpecialKey(tcell.KeyCtrlT) + Expect(len(browsh.Tabs)).To(Equal(2)) }) It("should be able to goto a new URL", func() { - Keyboard(testSiteURL + "/smorgasbord/another.html") - SpecialKey(tcell.KeyEnter) - Expect("Another").To(BeInFrameAt(21, 0)) + SpecialKey(tcell.KeyCtrlT) + GotoURL(testSiteURL + "/smorgasbord/another.html") + Expect("Another▄webpage").To(BeInFrameAt(1, 3)) }) It("should cycle to the next tab", func() { - Expect(" ").To(BeInFrameAt(0, 1)) - SpecialKey(tcell.KeyCtrlL) + GotoURL(testSiteURL + "/smorgasbord/") + SpecialKey(tcell.KeyCtrlT) GotoURL(testSiteURL + "/smorgasbord/another.html") triggerUserKeyFor("tty.keys.next-tab") - URL := testSiteURL + "/smorgasbord/ " - Expect(URL).To(BeInFrameAt(0, 1)) + Expect("Smörgåsbord").To(BeInFrameAt(0, 0)) }) }) }) diff --git a/interfacer/test/tty/vim_test.go b/interfacer/test/tty/vim_test.go index 3487a023..b52f7d73 100644 --- a/interfacer/test/tty/vim_test.go +++ b/interfacer/test/tty/vim_test.go @@ -41,9 +41,10 @@ var _ = Describe("Vim tests", func() { }) It("should cycle to the next tab", func() { + GotoURL(testSiteURL + "/smorgasbord/") Keyboard("t") GotoURL(testSiteURL + "/smorgasbord/another.html") - Keyboard("gt") + Keyboard("J") URL := testSiteURL + "/smorgasbord/ " Expect(URL).To(BeInFrameAt(0, 1)) }) From 26e9c6185f1e883c58ca9ff3814c7befa8001715 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Sun, 11 Nov 2018 23:27:54 +0900 Subject: [PATCH 26/77] Vim mode: convert unexported symbols to lowercase --- interfacer/src/browsh/ui.go | 24 +++---- interfacer/src/browsh/vim_mode.go | 115 ++++++++++++++---------------- 2 files changed, 64 insertions(+), 75 deletions(-) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index afc59e09..392d618c 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -110,30 +110,30 @@ func UrlBarFocus(on bool) { func overlayVimMode() { _, height := screen.Size() - switch vimMode { - case InsertMode: + switch currentVimMode { + case insertMode: writeString(0, height-1, "ins", tcell.StyleDefault) - case LinkMode: + case linkMode: writeString(0, height-1, "lnk", tcell.StyleDefault) - case LinkModeNewTab: + case linkModeNewTab: writeString(0, height-1, "LNK", tcell.StyleDefault) - case LinkModeCopy: + case linkModeCopy: writeString(0, height-1, "cp", tcell.StyleDefault) - case VisualMode: + case visualMode: writeString(0, height-1, "vis", tcell.StyleDefault) - case CaretMode: + case caretMode: writeString(0, height-1, "car", tcell.StyleDefault) writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) - case FindMode: + case findMode: writeString(0, height-1, "/"+findText, tcell.StyleDefault) - case MakeMarkMode: + case makeMarkMode: writeString(0, height-1, "mark", tcell.StyleDefault) - case GotoMarkMode: + case gotoMarkMode: writeString(0, height-1, "goto", tcell.StyleDefault) } - switch vimMode { - case LinkMode, LinkModeNewTab, LinkModeCopy: + switch currentVimMode { + case linkMode, linkModeNewTab, linkModeCopy: if !linkModeWithHints { findAndHighlightTextOnScreen(linkText) } diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 0ec04825..1a2f8630 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -14,22 +14,20 @@ import ( // TODO: A little description as to the respective responsibilties of this code versus the // vimium.js code. -// TODO: Capitalised variables mean that developers can expect them to be publically availably -// as part of the API of the package. I don't think that is the intended case here. -type VimMode int +type vimMode int const ( - NormalMode VimMode = iota + 1 - InsertMode - FindMode - LinkMode - LinkModeNewTab - LinkModeCopy - WaitMode - VisualMode - CaretMode - MakeMarkMode - GotoMarkMode + normalMode vimMode = iota + 1 + insertMode + findMode + linkMode + linkModeNewTab + linkModeCopy + waitMode + visualMode + caretMode + makeMarkMode + gotoMarkMode ) // TODO: What's a mark? @@ -40,7 +38,7 @@ type mark struct { yScroll int } -type HintRect struct { +type hintRect struct { Bottom int `json:"bottom"` Top int `json:"top"` Left int `json:"left"` @@ -51,34 +49,26 @@ type HintRect struct { } var ( - // vimMode - vimMode = NormalMode + currentVimMode = normalMode vimCommandsBindings = make(map[string]string) keyEvents = make([]*tcell.EventKey, 11) - //runeTime = make(map[rune]time.Time) - //lastRune rune - waitModeStartTime time.Time - findText string + waitModeStartTime time.Time + findText string // marks globalMarkMap = make(map[rune]*mark) localMarkMap = make(map[int]map[rune]*mark) - // position coordinate for caret mode + // Position coordinate for caret mode caretPos Coordinate - // for link modes + // For link modes linkText string - linkHintRects []HintRect + linkHintRects []hintRect linkHintKeys = "asdfwerxcv" linkHints []string - linkHintsToRects = make(map[string]*HintRect) + linkHintsToRects = make(map[string]*hintRect) linkModeWithHints = true linkHintWriteStringCalls *func() ) -// TODO: What's this for? -func init() { - setupLinkHints() -} - func setupLinkHints() { lowerAlpha := "abcdefghijklmnopqrstuvwxyz" missingAlpha := lowerAlpha @@ -166,12 +156,12 @@ func makeMark() *mark { } func goIntoWaitMode() { - vimMode = WaitMode + currentVimMode = waitMode waitModeStartTime = time.Now() } func updateLinkHintDisplay() { - linkHintsToRects = make(map[string]*HintRect) + linkHintsToRects = make(map[string]*hintRect) lh := len(linkHintRects) var ht string // List of closures @@ -203,7 +193,7 @@ func updateLinkHintDisplay() { rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht // Link hints are in upper case in new tab mode - if vimMode == LinkModeNewTab { + if currentVimMode == linkModeNewTab { htCopy = strings.ToUpper(htCopy) } @@ -225,7 +215,7 @@ func updateLinkHintDisplay() { func eraseLinkHints() { linkText = "" linkHintWriteStringCalls = nil - linkHintsToRects = make(map[string]*HintRect) + linkHintsToRects = make(map[string]*hintRect) linkHintRects = nil } @@ -251,24 +241,24 @@ func handleVimControl(ev *tcell.EventKey) { keyCombination := string(lastRune) + string(ev.Rune()) - switch vimMode { - case WaitMode: + switch currentVimMode { + case waitMode: if time.Since(waitModeStartTime) < time.Millisecond*1000 { return } - vimMode = NormalMode + currentVimMode = normalMode fallthrough - case NormalMode: + case normalMode: command = vimCommandsBindings[keyCombination] if len(command) == 0 { keyCombination := string(ev.Rune()) command = vimCommandsBindings[keyCombination] } - case InsertMode: + case insertMode: if isNormalModeKey(ev) { command = "normalMode" } - case VisualMode: + case visualMode: if isNormalModeKey(ev) { command = "normalMode" } else { @@ -282,7 +272,7 @@ func handleVimControl(ev *tcell.EventKey) { //clipboard } } - case CaretMode: + case caretMode: if isNormalModeKey(ev) { command = "normalMode" } else { @@ -318,7 +308,7 @@ func handleVimControl(ev *tcell.EventKey) { } } } - case MakeMarkMode: + case makeMarkMode: if unicode.IsLower(ev.Rune()) { if localMarkMap[CurrentTab.ID] == nil { localMarkMap[CurrentTab.ID] = make(map[rune]*mark) @@ -329,7 +319,7 @@ func handleVimControl(ev *tcell.EventKey) { } command = "normalMode" - case GotoMarkMode: + case gotoMarkMode: if mark, ok := globalMarkMap[ev.Rune()]; ok { gotoMark(mark) } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { @@ -339,13 +329,13 @@ func handleVimControl(ev *tcell.EventKey) { } command = "normalMode" - case FindMode: + case findMode: if isNormalModeKey(ev) { command = "normalMode" findText = "" } else { if ev.Key() == tcell.KeyEnter { - vimMode = NormalMode + currentVimMode = normalMode command = "findText" break } @@ -357,7 +347,7 @@ func handleVimControl(ev *tcell.EventKey) { findText += string(ev.Rune()) } } - case LinkMode, LinkModeNewTab, LinkModeCopy: + case linkMode, linkModeNewTab, linkModeCopy: if isNormalModeKey(ev) { command = "normalMode" eraseLinkHints() @@ -367,17 +357,16 @@ func handleVimControl(ev *tcell.EventKey) { if linkModeWithHints { if r, ok := linkHintsToRects[linkText]; ok { if r != nil { - switch vimMode { - case LinkMode: + switch currentVimMode { + case linkMode: if (*r).Height == 2 { generateLeftClickYHack((*r).Left, (*r).Top, true) } else { generateLeftClick((*r).Left, (*r).Top) } - case LinkModeNewTab: - //generateMiddleClick(r.Left, r.Top) + case linkModeNewTab: sendMessageToWebExtension("/new_tab," + r.Href) - case LinkModeCopy: + case linkModeCopy: clipboard.WriteAll(r.Href) } goIntoWaitMode() @@ -389,7 +378,7 @@ func handleVimControl(ev *tcell.EventKey) { if len(coords) == 1 { goIntoWaitMode() - if vimMode == LinkModeNewTab { + if currentVimMode == linkModeNewTab { generateMiddleClick(coords[0].X, coords[0].Y) } else { generateLeftClick(coords[0].X, coords[0].Y) @@ -397,7 +386,7 @@ func handleVimControl(ev *tcell.EventKey) { linkText = "" return } else if len(coords) == 0 { - vimMode = NormalMode + currentVimMode = normalMode linkText = "" return } @@ -481,15 +470,15 @@ func executeVimCommand(command string) { case "viewHelp": sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") case "openLinkInCurrentTab": - vimMode = LinkMode + currentVimMode = linkMode sendMessageToWebExtension("/tab_command,/get_clickable_hints") eraseLinkHints() case "openLinkInNewTab": - vimMode = LinkModeNewTab + currentVimMode = linkModeNewTab sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "copyLinkURL": - vimMode = LinkModeCopy + currentVimMode = linkModeCopy sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "findText": @@ -499,19 +488,19 @@ func executeVimCommand(command string) { case "findPrevious": sendMessageToWebExtension("/tab_command,/find_previous," + findText) case "makeMark": - vimMode = MakeMarkMode + currentVimMode = makeMarkMode case "gotoMark": - vimMode = GotoMarkMode + currentVimMode = gotoMarkMode case "insertMode": - vimMode = InsertMode + currentVimMode = insertMode case "findMode": - vimMode = FindMode + currentVimMode = findMode case "normalMode": - vimMode = NormalMode + currentVimMode = normalMode case "visualMode": - vimMode = VisualMode + currentVimMode = visualMode case "caretMode": - vimMode = CaretMode + currentVimMode = caretMode width, height := screen.Size() caretPos.X, caretPos.Y = width/2, height/2 } From af487aefe11d4002a71b711144a956a2d05ea837 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Mon, 12 Nov 2018 14:17:29 +0900 Subject: [PATCH 27/77] Vim mode: Small updates from PR review --- interfacer/src/browsh/browsh.go | 1 + interfacer/src/browsh/vim_mode.go | 14 +++++++------- webext/src/dom/commands_mixin.js | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index f25dde79..d43303c1 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -155,6 +155,7 @@ func TTYStart(injectedScreen tcell.Screen) { Log("Starting Browsh CLI client") go readStdin() startWebSocketServer() + setupLinkHints() } func toInt(char string) int { diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 1a2f8630..0543d5b7 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -54,7 +54,7 @@ var ( keyEvents = make([]*tcell.EventKey, 11) waitModeStartTime time.Time findText string - // marks + // Marks globalMarkMap = make(map[rune]*mark) localMarkMap = make(map[int]map[rune]*mark) // Position coordinate for caret mode @@ -81,9 +81,9 @@ func setupLinkHints() { missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) } - // missingAlpha contains all keys that aren't in linkHintKeys + // `missingAlpha` contains all keys that aren't in `linkHintKeys` // we use this to generate the last link hint key combinations, - // so this will only be used when we run out of linkHintKeys based + // so this will only be used when we run out of `linkHintKeys` based // link hint key combinations. for i := 0; i < len(missingAlpha); i++ { for j := 0; j < len(lowerAlpha); j++ { @@ -93,8 +93,8 @@ func setupLinkHints() { } // Moves the caret in CaretMode. -// isCaretAtBoundary is a function that tests for the reaching of the boundaries of the given axis. -// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. +// `isCaretAtBoundary` is a function that tests for the reaching of the boundaries of the given axis. +// The axis of motion is decided by giving a reference to `caretPos.X` or `caretPos.Y` as `valRef`. // The step size and direction is given by the value of step. func moveVimCaret(isCaretAtBoundary func() bool, valRef *int, step int) { var prevCell, nextCell, nextNextCell cell @@ -506,7 +506,7 @@ func executeVimCommand(command string) { } } -func searchScreenForText(text string) []Coordinate { +func searchVisibleScreenForText(text string) []Coordinate { var offsets = make([]Coordinate, 0) var splitString []string var r rune @@ -544,7 +544,7 @@ func findAndHighlightTextOnScreen(text string) []Coordinate { var x, y int var styling = tcell.StyleDefault - offsets := searchScreenForText(text) + offsets := searchVisibleScreenForText(text) for _, offset := range offsets { y = offset.Y x = offset.X diff --git a/webext/src/dom/commands_mixin.js b/webext/src/dom/commands_mixin.js index dd608c94..2a344774 100644 --- a/webext/src/dom/commands_mixin.js +++ b/webext/src/dom/commands_mixin.js @@ -124,6 +124,7 @@ export default MixinBase => this.followLinkLabeledPrevious(); } + // Eg; This goes from www.domain.com/topic/suptopic/ to www.domain.com/topic/ urlUp() { // this is taken from vimium's code var url = window.location.href; From 15f541c2dd46a81a1e37bee14d63a0aaaebbf5c5 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 13 Nov 2018 16:34:16 +0900 Subject: [PATCH 28/77] Travis CI: Bash timeout for integration tests --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4cd7bad9..2341a262 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,8 +37,8 @@ install: script: - cd $REPO_ROOT/webext && npm test - cd $REPO_ROOT/interfacer && go test $(find src/browsh -name *.go | grep -v windows) - - cd $REPO_ROOT/interfacer && go test test/tty/*.go -v -ginkgo.flakeAttempts=3 - - cd $REPO_ROOT/interfacer && go test test/http-server/*.go -v + - cd $REPO_ROOT/interfacer && timeout 9m go test test/tty/*.go -v -ginkgo.flakeAttempts=3 + - cd $REPO_ROOT/interfacer && timeout 9m go test test/http-server/*.go -v after_failure: - cat $REPO_ROOT/interfacer/test/tty/debug.log - cat $REPO_ROOT/interfacer/test/http-server/debug.log From 565e6f4c3cc66472977d34d2f07b70c13ba0a000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Tue, 13 Nov 2018 07:42:52 -0500 Subject: [PATCH 29/77] Fixed bug where keyEvents variable was initialized wrongly. This led to key combinations only working after a certain number of key strokes. --- interfacer/src/browsh/vim_mode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 0543d5b7..61edf878 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -51,7 +51,7 @@ type hintRect struct { var ( currentVimMode = normalMode vimCommandsBindings = make(map[string]string) - keyEvents = make([]*tcell.EventKey, 11) + keyEvents = make([]*tcell.EventKey, 0, 11) waitModeStartTime time.Time findText string // Marks From 9c668e83c913a6bbc5ad5a077a2c753bbc32fb09 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Wed, 14 Nov 2018 23:15:23 +0900 Subject: [PATCH 30/77] Travis: upload logs to text host This is because Travis' logs had 2 problems. 1. it doesn't capture the entire log output 2. it doesn't show logs when there's a timeout --- .travis.yml | 4 ++-- interfacer/src/browsh/ui.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2341a262..1c80b389 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,8 @@ script: - cd $REPO_ROOT/interfacer && timeout 9m go test test/tty/*.go -v -ginkgo.flakeAttempts=3 - cd $REPO_ROOT/interfacer && timeout 9m go test test/http-server/*.go -v after_failure: - - cat $REPO_ROOT/interfacer/test/tty/debug.log - - cat $REPO_ROOT/interfacer/test/http-server/debug.log + - cat $REPO_ROOT/interfacer/test/tty/debug.log | curl -F 'f:1=<-' ix.io + - cat $REPO_ROOT/interfacer/test/http-server/debug.log | curl -F 'f:1=<-' ix.io after_success: - $REPO_ROOT/contrib/release_if_new_version.sh diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 392d618c..eba37060 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -93,7 +93,9 @@ func urlBarFocusToggle() { } } -func UrlBarFocus(on bool) { +// Set the focus of the URL bar. Also used in tests to ensure the URL bar is in fact focussed as +// toggling doesn't guarantee that you will gain focus. +func URLBarFocus(on bool) { if !on { activeInputBox = nil urlInputBox.isActive = false From 748bf9d53f067f72573d05bb557d35d5b8290b0b Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Wed, 14 Nov 2018 23:29:23 +0900 Subject: [PATCH 31/77] Gofmt: some minor capitalisation --- interfacer/src/browsh/frame_builder.go | 2 +- interfacer/src/browsh/input_box.go | 2 +- interfacer/src/browsh/tab.go | 10 +++++----- interfacer/src/browsh/ui.go | 4 ++-- interfacer/test/tty/setup.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index e85e8693..2a2b3af0 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -312,7 +312,7 @@ func (f *frame) maybeFocusInputBox(x, y int) { left := inputBox.X right := inputBox.X + inputBox.Width if x >= left && x < right && y >= top && y < bottom { - UrlBarFocus(false) + URLBarFocus(false) inputBox.isActive = true activeInputBox = inputBox } diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index 431297c8..878ebe77 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -181,7 +181,7 @@ func (i *inputBox) handleEnterKey(modifier tcell.ModMask) { } else { sendMessageToWebExtension("/url_bar," + string(i.text)) } - UrlBarFocus(false) + URLBarFocus(false) } if i.isMultiLine() && modifier != tcell.ModAlt { i.cursorInsertRune([]rune("\n")[0]) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index 44893139..bf3035eb 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -91,8 +91,8 @@ func moveTabLeft(id int) { return } - for i, tabId := range tabsOrder { - if tabId == id { + for i, tabID := range tabsOrder { + if tabID == id { tabsOrder[i-1], tabsOrder[i] = tabsOrder[i], tabsOrder[i-1] break } @@ -106,8 +106,8 @@ func moveTabRight(id int) { return } - for i, tabId := range tabsOrder { - if tabId == id { + for i, tabID := range tabsOrder { + if tabID == id { tabsOrder[i+1], tabsOrder[i] = tabsOrder[i], tabsOrder[i+1] break } @@ -134,7 +134,7 @@ func createNewEmptyTab() { CurrentTab = tab CurrentTab.frame.resetCells() renderUI() - UrlBarFocus(true) + URLBarFocus(true) renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index eba37060..ba21865e 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -87,9 +87,9 @@ func renderURLBar() { func urlBarFocusToggle() { if urlInputBox.isActive { - UrlBarFocus(false) + URLBarFocus(false) } else { - UrlBarFocus(true) + URLBarFocus(true) } } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 78ae095b..494be463 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -126,7 +126,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { - browsh.UrlBarFocus(true) + browsh.URLBarFocus(true) Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() From fbb1cfc060cd783db994380a9926742526ac9190 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Wed, 14 Nov 2018 23:15:23 +0900 Subject: [PATCH 32/77] Travis: upload logs to text host This is because Travis' logs had 2 problems. 1. it doesn't capture the entire log output 2. it doesn't show logs when there's a timeout --- .travis.yml | 4 ++-- interfacer/src/browsh/ui.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2341a262..1c80b389 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,8 @@ script: - cd $REPO_ROOT/interfacer && timeout 9m go test test/tty/*.go -v -ginkgo.flakeAttempts=3 - cd $REPO_ROOT/interfacer && timeout 9m go test test/http-server/*.go -v after_failure: - - cat $REPO_ROOT/interfacer/test/tty/debug.log - - cat $REPO_ROOT/interfacer/test/http-server/debug.log + - cat $REPO_ROOT/interfacer/test/tty/debug.log | curl -F 'f:1=<-' ix.io + - cat $REPO_ROOT/interfacer/test/http-server/debug.log | curl -F 'f:1=<-' ix.io after_success: - $REPO_ROOT/contrib/release_if_new_version.sh diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 392d618c..eba37060 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -93,7 +93,9 @@ func urlBarFocusToggle() { } } -func UrlBarFocus(on bool) { +// Set the focus of the URL bar. Also used in tests to ensure the URL bar is in fact focussed as +// toggling doesn't guarantee that you will gain focus. +func URLBarFocus(on bool) { if !on { activeInputBox = nil urlInputBox.isActive = false From 2bf920bbd6dc5a032c4fe16ecaf98a1d44dffe7b Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Wed, 14 Nov 2018 23:29:23 +0900 Subject: [PATCH 33/77] Gofmt: some minor capitalisation --- interfacer/src/browsh/frame_builder.go | 2 +- interfacer/src/browsh/input_box.go | 2 +- interfacer/src/browsh/tab.go | 10 +++++----- interfacer/src/browsh/ui.go | 4 ++-- interfacer/test/tty/setup.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index e85e8693..2a2b3af0 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -312,7 +312,7 @@ func (f *frame) maybeFocusInputBox(x, y int) { left := inputBox.X right := inputBox.X + inputBox.Width if x >= left && x < right && y >= top && y < bottom { - UrlBarFocus(false) + URLBarFocus(false) inputBox.isActive = true activeInputBox = inputBox } diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index 556aac89..fe115e52 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -181,7 +181,7 @@ func (i *inputBox) handleEnterKey(modifier tcell.ModMask) { } else { sendMessageToWebExtension("/url_bar," + string(i.text)) } - UrlBarFocus(false) + URLBarFocus(false) } if i.isMultiLine() && modifier != tcell.ModAlt { i.cursorInsertRune([]rune("\n")[0]) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index 44893139..bf3035eb 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -91,8 +91,8 @@ func moveTabLeft(id int) { return } - for i, tabId := range tabsOrder { - if tabId == id { + for i, tabID := range tabsOrder { + if tabID == id { tabsOrder[i-1], tabsOrder[i] = tabsOrder[i], tabsOrder[i-1] break } @@ -106,8 +106,8 @@ func moveTabRight(id int) { return } - for i, tabId := range tabsOrder { - if tabId == id { + for i, tabID := range tabsOrder { + if tabID == id { tabsOrder[i+1], tabsOrder[i] = tabsOrder[i], tabsOrder[i+1] break } @@ -134,7 +134,7 @@ func createNewEmptyTab() { CurrentTab = tab CurrentTab.frame.resetCells() renderUI() - UrlBarFocus(true) + URLBarFocus(true) renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index eba37060..ba21865e 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -87,9 +87,9 @@ func renderURLBar() { func urlBarFocusToggle() { if urlInputBox.isActive { - UrlBarFocus(false) + URLBarFocus(false) } else { - UrlBarFocus(true) + URLBarFocus(true) } } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 78ae095b..494be463 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -126,7 +126,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { - browsh.UrlBarFocus(true) + browsh.URLBarFocus(true) Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() From e10510f634cfeeb501623d3dd4df148a1f593e58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Thu, 15 Nov 2018 23:52:14 +0100 Subject: [PATCH 34/77] Added vim feature for editing URL in new tab --- interfacer/src/browsh/config.go | 1 + interfacer/src/browsh/tab.go | 8 +++++++- interfacer/src/browsh/vim_mode.go | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go index 7405f1d5..0ffd9156 100644 --- a/interfacer/src/browsh/config.go +++ b/interfacer/src/browsh/config.go @@ -85,6 +85,7 @@ func setDefaults() { vimCommandsBindings["u"] = "scrollHalfPageUp" vimCommandsBindings["e"] = "editURL" vimCommandsBindings["ge"] = "editURL" + vimCommandsBindings["gE"] = "editURLInNewTab" vimCommandsBindings["H"] = "historyBack" vimCommandsBindings["L"] = "historyForward" vimCommandsBindings["J"] = "prevTab" diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index bf3035eb..e3f73035 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -123,18 +123,24 @@ func duplicateTab(id int) { // tab then we can't talk to it to tell it navigate. So we need to only create a real new // tab when we actually have a URL. func createNewEmptyTab() { + createNewEmptyTabWithURI("") +} + +func createNewEmptyTabWithURI(URI string) { if isNewEmptyTabActive() { return } newTab(-1) tab := Tabs[-1] tab.Title = "New Tab" - tab.URI = "" + tab.URI = URI tab.Active = true CurrentTab = tab CurrentTab.frame.resetCells() renderUI() URLBarFocus(true) + // Allows for typing directly at the end of URI + urlInputBox.selectionOff() renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 61edf878..6a112191 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -419,6 +419,8 @@ func executeVimCommand(command string) { doScroll(1, 0) case "editURL": urlBarFocusToggle() + case "editURLInNewTab": + createNewEmptyTabWithURI(CurrentTab.URI) case "firstTab": switchToTab(tabsOrder[0]) case "lastTab": From 3449ec12d2545f3b5061e85353563bbf105681c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Fri, 23 Nov 2018 10:08:11 +0100 Subject: [PATCH 35/77] Fixed bug in key event handling between vim modes, where the same key event could get interpreted repeatedly. Also some rewrites/improvements of the code. Key mappings can now contain control characters and meta keys with a vim-like notation. There's a hard insert mode, which disables all of browsh's shortcuts and requires 4 hits on ESC to leave. There's a new multiple link opening feature analogous to vimium, that's still incomplete. --- interfacer/src/browsh/config.go | 102 +++++++----- interfacer/src/browsh/tty.go | 8 +- interfacer/src/browsh/ui.go | 10 +- interfacer/src/browsh/vim_mode.go | 256 +++++++++++++++++++----------- 4 files changed, 238 insertions(+), 138 deletions(-) diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go index 0ffd9156..9e727559 100644 --- a/interfacer/src/browsh/config.go +++ b/interfacer/src/browsh/config.go @@ -75,50 +75,64 @@ func setDefaults() { viper.SetDefault("tty.keys.next-tab", []string{"\u001c", "28", "2"}) // Vim commands - vimCommandsBindings["gg"] = "scrollToTop" - vimCommandsBindings["G"] = "scrollToBottom" - vimCommandsBindings["j"] = "scrollDown" - vimCommandsBindings["k"] = "scrollUp" - vimCommandsBindings["h"] = "scrollLeft" - vimCommandsBindings["l"] = "scrollRight" - vimCommandsBindings["d"] = "scrollHalfPageDown" - vimCommandsBindings["u"] = "scrollHalfPageUp" - vimCommandsBindings["e"] = "editURL" - vimCommandsBindings["ge"] = "editURL" - vimCommandsBindings["gE"] = "editURLInNewTab" - vimCommandsBindings["H"] = "historyBack" - vimCommandsBindings["L"] = "historyForward" - vimCommandsBindings["J"] = "prevTab" - vimCommandsBindings["K"] = "nextTab" - vimCommandsBindings["r"] = "reload" - vimCommandsBindings["xx"] = "removeTab" - vimCommandsBindings["X"] = "restoreTab" - vimCommandsBindings["t"] = "newTab" - vimCommandsBindings["/"] = "findMode" - vimCommandsBindings["n"] = "findNext" - vimCommandsBindings["N"] = "findPrevious" - vimCommandsBindings["g0"] = "firstTab" - vimCommandsBindings["g$"] = "lastTab" - vimCommandsBindings["gu"] = "urlUp" - vimCommandsBindings["gU"] = "urlRoot" - vimCommandsBindings["<<"] = "moveTabLeft" - vimCommandsBindings[">>"] = "moveTabRight" - vimCommandsBindings["^"] = "previouslyVisitedTab" - vimCommandsBindings["m"] = "makeMark" - vimCommandsBindings["'"] = "gotoMark" - vimCommandsBindings["i"] = "insertMode" - vimCommandsBindings["yy"] = "copyURL" - vimCommandsBindings["p"] = "openClipboardURL" - vimCommandsBindings["P"] = "openClipboardURLInNewTab" - vimCommandsBindings["gi"] = "focusFirstTextInput" - vimCommandsBindings["f"] = "openLinkInCurrentTab" - vimCommandsBindings["F"] = "openLinkInNewTab" - vimCommandsBindings["yf"] = "copyLinkURL" - vimCommandsBindings["[["] = "followLinkLabeledPrevious" - vimCommandsBindings["]]"] = "followLinkLabeledNext" - vimCommandsBindings["yt"] = "duplicateTab" - vimCommandsBindings["v"] = "visualMode" - vimCommandsBindings["?"] = "viewHelp" + vimKeyMap["normal gg"] = "scrollToTop" + vimKeyMap["normal G"] = "scrollToBottom" + vimKeyMap["normal j"] = "scrollDown" + vimKeyMap["normal k"] = "scrollUp" + vimKeyMap["normal h"] = "scrollLeft" + vimKeyMap["normal l"] = "scrollRight" + vimKeyMap["normal d"] = "scrollHalfPageDown" + vimKeyMap["normal "] = "scrollHalfPageDown" + vimKeyMap["normal u"] = "scrollHalfPageUp" + vimKeyMap["normal "] = "scrollHalfPageUp" + vimKeyMap["normal e"] = "editURL" + vimKeyMap["normal ge"] = "editURL" + vimKeyMap["normal gE"] = "editURLInNewTab" + vimKeyMap["normal H"] = "historyBack" + vimKeyMap["normal L"] = "historyForward" + vimKeyMap["normal J"] = "prevTab" + vimKeyMap["normal K"] = "nextTab" + vimKeyMap["normal r"] = "reload" + vimKeyMap["normal xx"] = "removeTab" + vimKeyMap["normal X"] = "restoreTab" + vimKeyMap["normal t"] = "newTab" + vimKeyMap["normal T"] = "searchForTab" + vimKeyMap["normal /"] = "findMode" + vimKeyMap["normal n"] = "findNext" + vimKeyMap["normal N"] = "findPrevious" + vimKeyMap["normal g0"] = "firstTab" + vimKeyMap["normal g$"] = "lastTab" + vimKeyMap["normal gu"] = "urlUp" + vimKeyMap["normal gU"] = "urlRoot" + vimKeyMap["normal <<"] = "moveTabLeft" + vimKeyMap["normal >>"] = "moveTabRight" + vimKeyMap["normal ^"] = "previouslyVisitedTab" + vimKeyMap["normal m"] = "makeMark" + vimKeyMap["normal '"] = "gotoMark" + vimKeyMap["normal i"] = "insertMode" + vimKeyMap["normal I"] = "insertModeHard" + vimKeyMap["normal yy"] = "copyURL" + vimKeyMap["normal p"] = "openClipboardURL" + vimKeyMap["normal P"] = "openClipboardURLInNewTab" + vimKeyMap["normal gi"] = "focusFirstTextInput" + vimKeyMap["normal f"] = "openLinkInCurrentTab" + vimKeyMap["normal F"] = "openLinkInNewTab" + vimKeyMap["normal "] = "openMultipleLinksInNewTab" + vimKeyMap["normal yf"] = "copyLinkURL" + vimKeyMap["normal [["] = "followLinkLabeledPrevious" + vimKeyMap["normal ]]"] = "followLinkLabeledNext" + vimKeyMap["normal yt"] = "duplicateTab" + vimKeyMap["normal v"] = "visualMode" + vimKeyMap["normal ?"] = "viewHelp" + vimKeyMap["caret v"] = "visualMode" + vimKeyMap["caret h"] = "moveCaretLeft" + vimKeyMap["caret l"] = "moveCaretRight" + vimKeyMap["caret j"] = "moveCaretDown" + vimKeyMap["caret k"] = "moveCaretUp" + vimKeyMap["caret "] = "clickAtCaretPosition" + vimKeyMap["visual c"] = "caretMode" + vimKeyMap["visual o"] = "swapVisualModeCursorPosition" + vimKeyMap["visual y"] = "copyVisualModeSelection" } func loadConfig() { diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index f7663b5e..577cfb1a 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -57,7 +57,7 @@ func readStdin() { } } -func handleUserKeyPress(ev *tcell.EventKey) { +func handleShortcuts(ev *tcell.EventKey) { if CurrentTab == nil { if ev.Key() == tcell.KeyCtrlQ { quitBrowsh() @@ -88,6 +88,12 @@ func handleUserKeyPress(ev *tcell.EventKey) { if isKey("tty.keys.next-tab", ev) { nextTab() } +} + +func handleUserKeyPress(ev *tcell.EventKey) { + if currentVimMode != insertModeHard { + handleShortcuts(ev) + } if !urlInputBox.isActive { forwardKeyPress(ev) } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index ba21865e..ace224be 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -115,10 +115,14 @@ func overlayVimMode() { switch currentVimMode { case insertMode: writeString(0, height-1, "ins", tcell.StyleDefault) + case insertModeHard: + writeString(0, height-1, "INS", tcell.StyleDefault) case linkMode: writeString(0, height-1, "lnk", tcell.StyleDefault) case linkModeNewTab: writeString(0, height-1, "LNK", tcell.StyleDefault) + case linkModeMultipleNewTab: + writeString(0, height-1, "*LNK", tcell.StyleDefault) case linkModeCopy: writeString(0, height-1, "cp", tcell.StyleDefault) case visualMode: @@ -128,14 +132,14 @@ func overlayVimMode() { writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) case findMode: writeString(0, height-1, "/"+findText, tcell.StyleDefault) - case makeMarkMode: + case markModeMake: writeString(0, height-1, "mark", tcell.StyleDefault) - case gotoMarkMode: + case markModeGoto: writeString(0, height-1, "goto", tcell.StyleDefault) } switch currentVimMode { - case linkMode, linkModeNewTab, linkModeCopy: + case linkMode, linkModeNewTab, linkModeMultipleNewTab, linkModeCopy: if !linkModeWithHints { findAndHighlightTextOnScreen(linkText) } diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 6a112191..cee6ee14 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -19,15 +19,17 @@ type vimMode int const ( normalMode vimMode = iota + 1 insertMode + insertModeHard findMode linkMode linkModeNewTab + linkModeMultipleNewTab linkModeCopy waitMode visualMode caretMode - makeMarkMode - gotoMarkMode + markModeMake + markModeGoto ) // TODO: What's a mark? @@ -49,11 +51,13 @@ type hintRect struct { } var ( - currentVimMode = normalMode - vimCommandsBindings = make(map[string]string) - keyEvents = make([]*tcell.EventKey, 0, 11) - waitModeStartTime time.Time - findText string + currentVimMode = normalMode + vimKeyMap = make(map[string]string) + keyEvents = make([]*tcell.EventKey, 0, 11) + waitModeStartTime time.Time + waitModeMaxMilliseconds = 1000 + findText string + latestKeyCombination string // Marks globalMarkMap = make(map[rune]*mark) localMarkMap = make(map[int]map[rune]*mark) @@ -156,7 +160,7 @@ func makeMark() *mark { } func goIntoWaitMode() { - currentVimMode = waitMode + changeVimMode(waitMode) waitModeStartTime = time.Now() } @@ -219,96 +223,111 @@ func eraseLinkHints() { linkHintRects = nil } +func resetLinkHints() { + linkText = "" + updateLinkHintDisplay() +} + func isNormalModeKey(ev *tcell.EventKey) bool { - if ev.Key() == tcell.KeyESC { + if ev != nil && ev.Key() == tcell.KeyESC { return true } return false } -func handleVimControl(ev *tcell.EventKey) { - var lastRune rune - command := "" +func keyEventToString(ev *tcell.EventKey) string { + if ev == nil { + return "" + } + + r := string(ev.Rune()) + if ev.Modifiers()&tcell.ModAlt != 0 && ev.Modifiers()&tcell.ModCtrl != 0 { + return "" + } else if ev.Modifiers()&tcell.ModAlt != 0 { + return "" + } else if ev.Modifiers()&tcell.ModCtrl != 0 { + return "" + } + + switch ev.Key() { + case tcell.KeyEnter: + return "" + } + + return r +} + - if len(keyEvents) > 0 && keyEvents[0] != nil { - lastRune = keyEvents[len(keyEvents)-1].Rune() +func getNLastKeyEvent(n int) *tcell.EventKey { + if n < 0 || keyEvents == nil { + return nil } + if len(keyEvents) > n { + return keyEvents[len(keyEvents)-n-1] + } + return nil +} + +func mapVimKeyEvents(ev *tcell.EventKey, mapMode string) string { + var lastEvent *tcell.EventKey + command := "" keyEvents = append(keyEvents, ev) if len(keyEvents) > 10 { keyEvents = keyEvents[1:] } - keyCombination := string(lastRune) + string(ev.Rune()) + lastEvent = getNLastKeyEvent(1) + + latestKeyCombination = keyEventToString(lastEvent) + keyEventToString(ev) + + command = vimKeyMap[mapMode+" "+latestKeyCombination] + if len(command) == 0 { + latestKeyCombination = keyEventToString(ev) + command = vimKeyMap[mapMode+" "+latestKeyCombination] + } + if len(command) <= 0 { + latestKeyCombination = "" + } else { + // Since len(command) must be greather than 0 here, + // a key mapping did match, therefore we reset keyEvents + keyEvents = nil + } + return command +} + +func handleVimMode(ev *tcell.EventKey, mode string) string { + if isNormalModeKey(ev) { + return "normalMode" + } else { + return mapVimKeyEvents(ev, mode) + } +} +func handleVimControl(ev *tcell.EventKey) { + var command string switch currentVimMode { case waitMode: - if time.Since(waitModeStartTime) < time.Millisecond*1000 { + if time.Since(waitModeStartTime) < time.Millisecond*time.Duration(waitModeMaxMilliseconds) { return } - currentVimMode = normalMode + changeVimMode(normalMode) fallthrough case normalMode: - command = vimCommandsBindings[keyCombination] - if len(command) == 0 { - keyCombination := string(ev.Rune()) - command = vimCommandsBindings[keyCombination] - } + command = mapVimKeyEvents(ev, "normal") case insertMode: - if isNormalModeKey(ev) { - command = "normalMode" - } - case visualMode: - if isNormalModeKey(ev) { + command = handleVimMode(ev, "insert") + case insertModeHard: + if isNormalModeKey(ev) && isNormalModeKey(getNLastKeyEvent(0)) && isNormalModeKey(getNLastKeyEvent(1)) && isNormalModeKey(getNLastKeyEvent(2)) { command = "normalMode" } else { - if ev.Rune() == 'c' { - command = "caretMode" - } - if ev.Rune() == 'o' { - //swap cursor position begin->end or end->begin - } - if ev.Rune() == 'y' { - //clipboard - } + command = mapVimKeyEvents(ev, "insertHard") } + case visualMode: + command = handleVimMode(ev, "visual") case caretMode: - if isNormalModeKey(ev) { - command = "normalMode" - } else { - switch ev.Key() { - case tcell.KeyEnter: - generateLeftClick(caretPos.X, caretPos.Y-uiHeight) - } - switch ev.Rune() { - case 'v': - command = "visualMode" - case 'h': - moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) - case 'l': - width, _ := screen.Size() - moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) - case 'k': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) - if caretPos.Y < uiHeight { - command = "scrollHalfPageUp" - if CurrentTab.frame.yScroll == 0 { - caretPos.Y = uiHeight - } else { - caretPos.Y += (height - uiHeight) / 2 - } - } - case 'j': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) - if caretPos.Y > height-uiHeight { - command = "scrollHalfPageDown" - caretPos.Y -= (height - uiHeight) / 2 - } - } - } - case makeMarkMode: + command = handleVimMode(ev, "caret") + case markModeMake: if unicode.IsLower(ev.Rune()) { if localMarkMap[CurrentTab.ID] == nil { localMarkMap[CurrentTab.ID] = make(map[rune]*mark) @@ -319,7 +338,7 @@ func handleVimControl(ev *tcell.EventKey) { } command = "normalMode" - case gotoMarkMode: + case markModeGoto: if mark, ok := globalMarkMap[ev.Rune()]; ok { gotoMark(mark) } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { @@ -335,7 +354,7 @@ func handleVimControl(ev *tcell.EventKey) { findText = "" } else { if ev.Key() == tcell.KeyEnter { - currentVimMode = normalMode + changeVimMode(normalMode) command = "findText" break } @@ -347,7 +366,7 @@ func handleVimControl(ev *tcell.EventKey) { findText += string(ev.Rune()) } } - case linkMode, linkModeNewTab, linkModeCopy: + case linkMode, linkModeNewTab, linkModeMultipleNewTab, linkModeCopy: if isNormalModeKey(ev) { command = "normalMode" eraseLinkHints() @@ -366,6 +385,9 @@ func handleVimControl(ev *tcell.EventKey) { } case linkModeNewTab: sendMessageToWebExtension("/new_tab," + r.Href) + case linkModeMultipleNewTab: + resetLinkHints() + return case linkModeCopy: clipboard.WriteAll(r.Href) } @@ -386,7 +408,7 @@ func handleVimControl(ev *tcell.EventKey) { linkText = "" return } else if len(coords) == 0 { - currentVimMode = normalMode + changeVimMode(normalMode) linkText = "" return } @@ -394,13 +416,17 @@ func handleVimControl(ev *tcell.EventKey) { } } - if len(command) > 0 { - executeVimCommand(command) - } + executeVimCommand(command) } func executeVimCommand(command string) { - switch command { + if len(command) == 0 { + return + } + + currentCommand := command + command = "" + switch currentCommand { case "urlUp": sendMessageToWebExtension("/tab_command,/url_up") case "urlRoot": @@ -472,15 +498,19 @@ func executeVimCommand(command string) { case "viewHelp": sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") case "openLinkInCurrentTab": - currentVimMode = linkMode + changeVimMode(linkMode) sendMessageToWebExtension("/tab_command,/get_clickable_hints") eraseLinkHints() case "openLinkInNewTab": - currentVimMode = linkModeNewTab + changeVimMode(linkModeNewTab) + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "openMultipleLinksInNewTab": + changeVimMode(linkModeMultipleNewTab) sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "copyLinkURL": - currentVimMode = linkModeCopy + changeVimMode(linkModeCopy) sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "findText": @@ -490,22 +520,68 @@ func executeVimCommand(command string) { case "findPrevious": sendMessageToWebExtension("/tab_command,/find_previous," + findText) case "makeMark": - currentVimMode = makeMarkMode + changeVimMode(markModeMake) case "gotoMark": - currentVimMode = gotoMarkMode + changeVimMode(markModeGoto) case "insertMode": - currentVimMode = insertMode + changeVimMode(insertMode) + case "insertModeHard": + changeVimMode(insertModeHard) case "findMode": - currentVimMode = findMode + changeVimMode(findMode) case "normalMode": - currentVimMode = normalMode + changeVimMode(normalMode) + // Visual mode case "visualMode": - currentVimMode = visualMode + changeVimMode(visualMode) + case "swapVisualModeCursorPosition": + // Stub + case "copyVisualModeSelection": + // Caret mode case "caretMode": - currentVimMode = caretMode + changeVimMode(caretMode) width, height := screen.Size() caretPos.X, caretPos.Y = width/2, height/2 + case "clickAtCaretPosition": + generateLeftClick(caretPos.X, caretPos.Y-uiHeight) + case "moveCaretLeft": + moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) + case "moveCaretRight": + width, _ := screen.Size() + moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) + case "moveCaretUp": + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) + if caretPos.Y < uiHeight { + command = "scrollHalfPageUp" + if CurrentTab.frame.yScroll == 0 { + caretPos.Y = uiHeight + } else { + caretPos.Y += (height - uiHeight) / 2 + } + } + case "moveCaretDown": + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) + if caretPos.Y > height-uiHeight { + command = "scrollHalfPageDown" + caretPos.Y -= (height - uiHeight) / 2 + } } + + // A command can spawn another + executeVimCommand(command) +} + +func changeVimMode(mode vimMode) { + if currentVimMode == mode { + // No change + return + } + + currentVimMode = mode + // Reset keyEvents + keyEvents = nil } func searchVisibleScreenForText(text string) []Coordinate { From e88b42a914cea7b116966ae06a1b56f88db24db9 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Thu, 13 Jun 2019 10:46:18 +0300 Subject: [PATCH 36/77] Create FUNDING.yml --- .github/FUNDING.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..423f8364 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: [tombh] +patreon: browsh From 76e7eb1ac62563a2589b58a14e22b523f8a26491 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 15 Jun 2019 21:13:17 -0700 Subject: [PATCH 37/77] use test script from master --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index a338ed51..69c6a467 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,8 +37,8 @@ install: script: - cd $REPO_ROOT/webext && npm test - cd $REPO_ROOT/interfacer && go test $(find src/browsh -name *.go | grep -v windows) - - cd $REPO_ROOT/interfacer && timeout 9m go test test/tty/*.go -v -ginkgo.flakeAttempts=3 - - cd $REPO_ROOT/interfacer && timeout 9m go test test/http-server/*.go -v + - cd $REPO_ROOT/interfacer && go test test/tty/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 + - cd $REPO_ROOT/interfacer && go test test/http-server/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 after_failure: - cat $REPO_ROOT/interfacer/test/tty/debug.log | curl -F 'f:1=<-' ix.io - cat $REPO_ROOT/interfacer/test/http-server/debug.log | curl -F 'f:1=<-' ix.io From 36ac818ceb14b917a1a077f8d4346ed6ab9a6089 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 16 Jun 2019 17:39:30 -0700 Subject: [PATCH 38/77] move vim test into tty test seems helpful --- interfacer/src/browsh/comms.go | 2 +- interfacer/test/tty/tty_test.go | 27 +++++++++++++++++ interfacer/test/tty/vim_test.go | 52 --------------------------------- 3 files changed, 28 insertions(+), 53 deletions(-) delete mode 100644 interfacer/test/tty/vim_test.go diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index 95d88a0e..10c07df5 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -62,7 +62,7 @@ func webSocketReader(ws *websocket.Conn) { triggerSocketWriterClose() return } - Shutdown(err) + Shutdown(errors.New(err.Error())) } } } diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index b1380472..4415d3b5 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -27,6 +27,19 @@ var _ = Describe("Core functionality", func() { }) Describe("Interaction", func() { + It("should navigate to a new page by using a link hint", func() { + Expect("Another▄page").To(BeInFrameAt(12, 18)) + Keyboard("f") + Keyboard("a") + Expect("Another").To(BeInFrameAt(0, 0)) + }) + + It("should scroll the page by one line", func() { + Expect("[ˈsmœrɡɔsˌbuːɖ])▄is▄a").To(BeInFrameAt(12, 11)) + Keyboard("j") + Expect("type▄of▄Scandinavian▄").To(BeInFrameAt(12, 11)) + }) + It("should navigate to a new page by using the URL bar", func() { SpecialKey(tcell.KeyCtrlL) Keyboard(testSiteURL + "/smorgasbord/another.html") @@ -149,6 +162,20 @@ var _ = Describe("Core functionality", func() { triggerUserKeyFor("tty.keys.next-tab") Expect("Smörgåsbord").To(BeInFrameAt(0, 0)) }) + + It("should create a new tab", func() { + Keyboard("t") + Expect("New Tab").To(BeInFrameAt(21, 0)) + }) + + It("should cycle to the next tab", func() { + GotoURL(testSiteURL + "/smorgasbord/") + Keyboard("t") + GotoURL(testSiteURL + "/smorgasbord/another.html") + Keyboard("J") + URL := testSiteURL + "/smorgasbord/ " + Expect(URL).To(BeInFrameAt(0, 1)) + }) }) }) }) diff --git a/interfacer/test/tty/vim_test.go b/interfacer/test/tty/vim_test.go deleted file mode 100644 index b52f7d73..00000000 --- a/interfacer/test/tty/vim_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestVim(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Integration tests") -} - -var _ = Describe("Vim tests", func() { - BeforeEach(func() { - GotoURL(testSiteURL + "/smorgasbord/") - }) - - It("should navigate to a new page by using a link hint", func() { - Expect("Another▄page").To(BeInFrameAt(12, 18)) - Keyboard("f") - Keyboard("a") - Expect("Another").To(BeInFrameAt(0, 0)) - }) - - It("should scroll the page by one line", func() { - Expect("[ˈsmœrɡɔsˌbuːɖ])▄is▄a").To(BeInFrameAt(12, 11)) - Keyboard("j") - Expect("type▄of▄Scandinavian▄").To(BeInFrameAt(12, 11)) - }) - - Describe("Tabs", func() { - BeforeEach(func() { - ensureOnlyOneTab() - }) - - It("should create a new tab", func() { - Keyboard("t") - Expect("New Tab").To(BeInFrameAt(21, 0)) - }) - - It("should cycle to the next tab", func() { - GotoURL(testSiteURL + "/smorgasbord/") - Keyboard("t") - GotoURL(testSiteURL + "/smorgasbord/another.html") - Keyboard("J") - URL := testSiteURL + "/smorgasbord/ " - Expect(URL).To(BeInFrameAt(0, 1)) - }) - }) -}) From f290601e115934c09f48d4312663cca853a8cdb8 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Tue, 18 Jun 2019 19:05:08 +0300 Subject: [PATCH 39/77] Fix for Viper's lowercasing of config keys :/ See spf13/viper#635 --- interfacer/src/browsh/config_sample.go | 11 +++++++++-- interfacer/src/browsh/firefox.go | 8 +++++--- interfacer/src/browsh/version.go | 2 +- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/interfacer/src/browsh/config_sample.go b/interfacer/src/browsh/config_sample.go index 5ecaf303..9ee24b9f 100644 --- a/interfacer/src/browsh/config_sample.go +++ b/interfacer/src/browsh/config_sample.go @@ -42,8 +42,15 @@ use-existing = false with-gui = false # Config that you might usually set through Firefox's 'about:config' page -[firefox-config] -# "privacy.resistFingerprinting" = true +# Note that string must be wrapped in quotes +# preferences = [ +# "privacy.resistFingerprinting=true", +# "network.proxy.http='localhost'", +# "network.proxy.ssl='localhost'", +# "network.proxy.http_port=8118", +# "network.proxy.ssl_port=8118", +# "network.proxy.type=1" +# ] [tty] # The time in milliseconds between requesting a new TTY-sized pixel frame. diff --git a/interfacer/src/browsh/firefox.go b/interfacer/src/browsh/firefox.go index d78b4128..f93fc467 100644 --- a/interfacer/src/browsh/firefox.go +++ b/interfacer/src/browsh/firefox.go @@ -258,7 +258,8 @@ func readMarionette() { buffer := make([]byte, 4096) count, err := marionette.Read(buffer) if err != nil { - Shutdown(err) + Log("Error reading from Marionette connection") + return } Log("FF-MRNT: " + string(buffer[:count])) } @@ -277,8 +278,9 @@ func setDefaultFirefoxPreferences() { for key, value := range defaultFFPrefs { setFFPreference(key, value) } - for key, value := range viper.GetStringMapString("firefox-config") { - setFFPreference(key, value) + for _, pref := range viper.GetStringSlice("firefox.preferences") { + parts := strings.SplitN(pref, "=", 2) + setFFPreference(parts[0], parts[1]) } } diff --git a/interfacer/src/browsh/version.go b/interfacer/src/browsh/version.go index b8072922..341f6a12 100644 --- a/interfacer/src/browsh/version.go +++ b/interfacer/src/browsh/version.go @@ -1,3 +1,3 @@ package browsh -var browshVersion = "1.6.0" +var browshVersion = "1.6.1" From a937e46000819ab1e71e563819ce5d8ef394f494 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 18 Jun 2019 20:19:34 -0700 Subject: [PATCH 40/77] test --- interfacer/src/browsh/browsh.go | 2 +- interfacer/test/tty/matchers.go | 3 ++- interfacer/test/tty/setup.go | 3 +-- webext/src/dom/manager.js | 27 ++++++++++++++++++++++----- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index 3f600e1d..2f4c324e 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -23,7 +23,7 @@ import ( var ( logo = ` -//// //// + //// //// / / / / // // // // ,,,,,,,, diff --git a/interfacer/test/tty/matchers.go b/interfacer/test/tty/matchers.go index 9eb4e7d3..2820846b 100644 --- a/interfacer/test/tty/matchers.go +++ b/interfacer/test/tty/matchers.go @@ -31,7 +31,8 @@ func (matcher *textInFrameMatcher) Match(actual interface{}) (success bool, err if matcher.found == text { return true, nil } - time.Sleep(100 * time.Millisecond) + fmt.Errorf("try...") + time.Sleep(500 * time.Millisecond) } return false, fmt.Errorf("Timeout. Expected\n\t%#v\nto be in the Browsh frame, but found\n\t%#v", text, matcher.found) } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 628f4e96..31fb689e 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -232,14 +232,13 @@ func initBrowsh() { browsh.IsTesting = true simScreen = tcell.NewSimulationScreen("UTF-8") browsh.Initialise() - } func stopFirefox() { browsh.Log("Attempting to kill all firefox processes") browsh.IsConnectedToWebExtension = false browsh.Shell(rootDir + "/webext/contrib/firefoxheadless.sh kill") - time.Sleep(500 * time.Millisecond) + time.Sleep(1500 * time.Millisecond) } func runeCount(text string) int { diff --git a/webext/src/dom/manager.js b/webext/src/dom/manager.js index 82c06760..67c95927 100644 --- a/webext/src/dom/manager.js +++ b/webext/src/dom/manager.js @@ -211,15 +211,32 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { let target = document.querySelector("body"); let observer = new MutationObserver(mutations => { mutations.forEach(mutation => { + if (!target) { + const nodes = Array.from(mutation.addedNodes); + for(let node of nodes) { + if(node.matches && node.matches("body")) { + target = node; + observer.observe(target, { + subtree: true, + characterData: true, + childList: true, + }); + break; + } + } + } this.log("!!MUTATION!!", mutation); this._debouncedSmallTextFrame(); }); }); - observer.observe(target, { - subtree: true, - characterData: true, - childList: true - }); + + if (target) { + observer.observe(target, { + subtree: true, + characterData: true, + childList: true + }); + } } _listenForBackgroundMessages() { From 9797f400c0933b48053f730b94038eee3f94841e Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 15 Jun 2019 10:39:39 -0700 Subject: [PATCH 41/77] clarify dev path --- README.md | 4 ++-- interfacer/src/browsh/comms.go | 2 +- webext/src/background/manager.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b3a2f242..1377b532 100644 --- a/README.md +++ b/README.md @@ -67,12 +67,12 @@ with symlinks. Anyway, to install the dependencies use: `dep ensure` inside the Then the ideal setup for development is: * have Webpack watch the JS code so that it rebuilds automatically: - `webpack --watch` + `webext> webpack --watch` * run the CLI client without giving it the responsibility to launch Firefox: `go run ./interfacer/src/main.go --firefox.use-existing --debug` * have Mozilla's handy `web-ext` tool run Firefox and reinstall the webextension everytime webpack rebuilds it: (in `webext/dist`) - `web-ext run --verbose` + `webext/dist> web-ext run --verbose` For generic Linux systems you can follow [this guide](https://github.com/browsh-org/browsh/blob/master/contrib/setup_linux_build_environment.md) on how to setup a build environment, that you may be able to adapt for other systems as well. diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index 9bfada9c..a4b0bb50 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -128,7 +128,7 @@ func webSocketWriter(ws *websocket.Conn) { defer ws.Close() for { message = <-stdinChannel - Log(fmt.Sprintf("TTY sending: %s", message)) + //Log(fmt.Sprintf("TTY sending: %s", message)) if err := ws.WriteMessage(websocket.TextMessage, []byte(message)); err != nil { if err == websocket.ErrCloseSent { Log("Socket writer detected that the browser closed the websocket") diff --git a/webext/src/background/manager.js b/webext/src/background/manager.js index aa54a430..75db4e19 100644 --- a/webext/src/background/manager.js +++ b/webext/src/background/manager.js @@ -66,7 +66,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { _listenForTerminalMessages() { this.log("Starting to listen to TTY"); this.terminal.addEventListener("message", event => { - this.log("Message from terminal: " + event.data); + //this.log("message from terminal: " + event.data); this.handleTerminalMessage(event.data); }); } From d9251ec25c6027764e732fb7aaa3a740331662d1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 19 Jun 2019 21:25:12 -0700 Subject: [PATCH 42/77] timestamp, wait for body --- interfacer/src/browsh/comms.go | 3 ++- webext/src/dom/commands_mixin.js | 3 +++ webext/src/dom/manager.js | 27 ++++++++++++++++++++++----- webext/src/dom/text_builder.js | 3 +++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index a4b0bb50..40fe3ed4 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -3,6 +3,7 @@ package browsh import ( "encoding/json" "fmt" + "time" "net/http" "strings" @@ -89,7 +90,7 @@ func handleWebextensionCommand(message []byte) { case "/screenshot": saveScreenshot(parts[1]) default: - Log("WEBEXT: " + string(message)) + Log(time.Now().Format("2006-01-02T15:04:05.999Z") + " WEBEXT: " + string(message)) } } diff --git a/webext/src/dom/commands_mixin.js b/webext/src/dom/commands_mixin.js index 10b357d2..745f2ddc 100644 --- a/webext/src/dom/commands_mixin.js +++ b/webext/src/dom/commands_mixin.js @@ -192,6 +192,9 @@ export default MixinBase => dom_x - window.scrollX, dom_y - window.scrollY ); + if (!element) { + return; + } element.focus(); var clickEvent = document.createEvent("MouseEvents"); clickEvent.initMouseEvent( diff --git a/webext/src/dom/manager.js b/webext/src/dom/manager.js index 0855b484..7a10b7c4 100644 --- a/webext/src/dom/manager.js +++ b/webext/src/dom/manager.js @@ -129,6 +129,7 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { _setupInteractiveMode() { this._setupDebouncedFunctions(); this._startMutationObserver(); + // TODO: wait until body exists this.sendAllBigFrames(); // TODO: // Disabling CSS transitions is not easy, many pages won't even render @@ -211,15 +212,31 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { let target = document.querySelector("body"); let observer = new MutationObserver(mutations => { mutations.forEach(mutation => { + if (!target) { + const nodes = Array.from(mutation.addedNodes); + for(let node of nodes) { + if(node.matches && node.matches("body")) { + target = node; + observer.observe(target, { + subtree: true, + characterData: true, + childList: true, + }); + break; + } + } + } this.log("!!MUTATION!!", mutation); this._debouncedSmallTextFrame(); }); }); - observer.observe(target, { - subtree: true, - characterData: true, - childList: true - }); + if (target) { + observer.observe(target, { + subtree: true, + characterData: true, + childList: true + }); + } } _listenForBackgroundMessages() { diff --git a/webext/src/dom/text_builder.js b/webext/src/dom/text_builder.js index 73387426..10065758 100644 --- a/webext/src/dom/text_builder.js +++ b/webext/src/dom/text_builder.js @@ -78,6 +78,9 @@ export default class extends utils.mixins(CommonMixin, SerialiseMixin) { // Search through every node in the DOM looking for displayable text. __getTextNodes() { + if (!document.body) { + return; + } this._text_nodes = []; const walker = document.createTreeWalker( document.body, From 7a39926fa6bc31d8416cab22a4af885de5f6baf4 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 23 Jun 2019 11:01:36 -0700 Subject: [PATCH 43/77] add test delay for tab --- interfacer/test/tty/tty_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index f31f2445..d569552c 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -2,6 +2,7 @@ package test import ( "testing" + "time" "github.com/gdamore/tcell" . "github.com/onsi/ginkgo" @@ -128,6 +129,7 @@ var _ = Describe("Showing a basic webpage", func() { Describe("Tabs", func() { BeforeEach(func() { SpecialKey(tcell.KeyCtrlT) + time.Sleep(1000 * time.Millisecond) }) AfterEach(func() { From 0c57d3cecc286978121ab8b920edbf48e2da0289 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 23 Jun 2019 18:19:53 -0700 Subject: [PATCH 44/77] better logging and improve tab test stability --- interfacer/src/browsh/browsh.go | 3 ++- interfacer/src/browsh/comms.go | 7 +++---- interfacer/src/browsh/firefox.go | 4 ++-- interfacer/src/browsh/frame_builder.go | 8 ++++++++ interfacer/src/browsh/input_multiline.go | 4 +++- interfacer/test/tty/setup.go | 11 ++++++++--- interfacer/test/tty/tty_test.go | 4 ++-- webext/src/background/manager.js | 2 +- 8 files changed, 29 insertions(+), 14 deletions(-) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index d3f2dc5f..ad2b2ef7 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -3,6 +3,7 @@ package browsh import ( "encoding/base64" "fmt" + "time" "io/ioutil" "net/url" "os" @@ -72,7 +73,7 @@ func Log(msg string) { } defer f.Close() - msg = msg + "\n" + msg = time.Now().Format("01-02T15:04:05.999 ") + msg + "\n" if _, wErr := f.WriteString(msg); wErr != nil { Shutdown(wErr) } diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index 40fe3ed4..aa4f06de 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -3,7 +3,6 @@ package browsh import ( "encoding/json" "fmt" - "time" "net/http" "strings" @@ -63,7 +62,7 @@ func webSocketReader(ws *websocket.Conn) { triggerSocketWriterClose() return } - Shutdown(err) + Shutdown(errors.New(err.Error())) } } } @@ -90,7 +89,7 @@ func handleWebextensionCommand(message []byte) { case "/screenshot": saveScreenshot(parts[1]) default: - Log(time.Now().Format("2006-01-02T15:04:05.999Z") + " WEBEXT: " + string(message)) + Log("IGNORE " + string(message)) } } @@ -129,7 +128,7 @@ func webSocketWriter(ws *websocket.Conn) { defer ws.Close() for { message = <-stdinChannel - //Log(fmt.Sprintf("TTY sending: %s", message)) + // chatty Log(fmt.Sprintf("TTY sending: %s", message)) if err := ws.WriteMessage(websocket.TextMessage, []byte(message)); err != nil { if err == websocket.ErrCloseSent { Log("Socket writer detected that the browser closed the websocket") diff --git a/interfacer/src/browsh/firefox.go b/interfacer/src/browsh/firefox.go index 00a9d724..f1b4b138 100644 --- a/interfacer/src/browsh/firefox.go +++ b/interfacer/src/browsh/firefox.go @@ -81,7 +81,7 @@ func startHeadlessFirefox() { } in := bufio.NewScanner(stdout) for in.Scan() { - Log("FF-CONSOLE: " + in.Text()) + Log("start headless FF-CONSOLE: " + in.Text()) } } @@ -179,7 +179,7 @@ func startWERFirefox() { strings.Contains(in.Text(), "dbus") { continue } - Log("FF-CONSOLE: " + in.Text()) + Log("start WER FF-CONSOLE: " + in.Text()) } Log("WER Firefox unexpectedly closed") } diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index afb53c38..d03b6974 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -89,6 +89,14 @@ func (f *frame) buildFrameText(incoming incomingFrameText) { if !f.isIncomingFrameTextValid(incoming) { return } + + var s = "/frame_text "; + for _,c := range incoming.Text { + if c != "" { + s = s + c + } + } + Log(s) f.updateInputBoxes(incoming) f.populateFrameText(incoming) } diff --git a/interfacer/src/browsh/input_multiline.go b/interfacer/src/browsh/input_multiline.go index d7696b55..dc21cf31 100644 --- a/interfacer/src/browsh/input_multiline.go +++ b/interfacer/src/browsh/input_multiline.go @@ -28,7 +28,9 @@ func (m *multiLine) convert() []rune { } if m.isInsideWord() { // TODO: This sometimes causes a panic :/ - m.currentWordish += m.currentCharacter + if m.currentCharacter != "" { + m.currentWordish += m.currentCharacter + } } else { m.addWhitespace() } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index b5b0b010..809ac196 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -112,6 +112,7 @@ func waitForNextFrame() { func WaitForText(text string, x, y int) { var found string start := time.Now() + browsh.Log("expect " + text) for time.Since(start) < perTestTimeout { found = GetText(x, y, runeCount(text)) if found == text { @@ -132,6 +133,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { time.Sleep(1000 * time.Millisecond) for time.Since(start) < maxTime { if browsh.CurrentTab != nil { + browsh.Log("pageload " + browsh.CurrentTab.PageState) if browsh.CurrentTab.PageState == "parsing_complete" { time.Sleep(200 * time.Millisecond) return @@ -144,6 +146,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { + browsh.Log("gotourl " + url) SpecialKey(tcell.KeyCtrlL) Keyboard(url) SpecialKey(tcell.KeyEnter) @@ -243,18 +246,19 @@ func runeCount(text string) int { } var _ = ginkgo.BeforeEach(func() { + browsh.Log("\n---------") + browsh.Log(ginkgo.CurrentGinkgoTestDescription().FullTestText) + browsh.Log("---------") browsh.Log("Attempting to restart WER Firefox...") stopFirefox() browsh.ResetTabs() browsh.StartFirefox() sleepUntilPageLoad(startupWait) browsh.IsMonochromeMode = false - browsh.Log("\n---------") - browsh.Log(ginkgo.CurrentGinkgoTestDescription().FullTestText) - browsh.Log("---------") }) var _ = ginkgo.BeforeSuite(func() { + browsh.Log("BeforeSuite---------") os.Truncate(framesLogFile, 0) initTerm() initBrowsh() @@ -269,4 +273,5 @@ var _ = ginkgo.BeforeSuite(func() { var _ = ginkgo.AfterSuite(func() { stopFirefox() + browsh.Log("AfterSuite--------------") }) diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index d569552c..0610a83b 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -2,7 +2,6 @@ package test import ( "testing" - "time" "github.com/gdamore/tcell" . "github.com/onsi/ginkgo" @@ -129,7 +128,6 @@ var _ = Describe("Showing a basic webpage", func() { Describe("Tabs", func() { BeforeEach(func() { SpecialKey(tcell.KeyCtrlT) - time.Sleep(1000 * time.Millisecond) }) AfterEach(func() { @@ -138,6 +136,8 @@ var _ = Describe("Showing a basic webpage", func() { It("should create a new tab", func() { Expect("New Tab").To(BeInFrameAt(21, 0)) + // need this to make tcell to work for the next round + SpecialKey(tcell.KeyCtrlL) }) It("should be able to goto a new URL", func() { diff --git a/webext/src/background/manager.js b/webext/src/background/manager.js index 75db4e19..97049aa6 100644 --- a/webext/src/background/manager.js +++ b/webext/src/background/manager.js @@ -66,7 +66,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { _listenForTerminalMessages() { this.log("Starting to listen to TTY"); this.terminal.addEventListener("message", event => { - //this.log("message from terminal: " + event.data); + // chatty this.log("message from terminal: " + event.data); this.handleTerminalMessage(event.data); }); } From b9b627046c2055f8c2387ac8a52fe7d7cf5138b6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 23 Jun 2019 21:30:46 -0700 Subject: [PATCH 45/77] merge vim mode code --- .travis.yml | 4 +-- interfacer/src/browsh/vim_mode.go | 32 ++++++++++++++++++------ interfacer/test/sites/links.html | 32 ++++++++++++++++++++++++ interfacer/test/tty/matchers.go | 3 +-- interfacer/test/tty/setup.go | 21 ++++++++++------ interfacer/test/tty/tty_test.go | 41 +++++++++++++++++++++++-------- 6 files changed, 105 insertions(+), 28 deletions(-) create mode 100644 interfacer/test/sites/links.html diff --git a/.travis.yml b/.travis.yml index 69c6a467..aac01f5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,8 @@ script: - cd $REPO_ROOT/interfacer && go test test/tty/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 - cd $REPO_ROOT/interfacer && go test test/http-server/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 after_failure: - - cat $REPO_ROOT/interfacer/test/tty/debug.log | curl -F 'f:1=<-' ix.io - - cat $REPO_ROOT/interfacer/test/http-server/debug.log | curl -F 'f:1=<-' ix.io + - cat $REPO_ROOT/interfacer/test/tty/debug.log + - cat $REPO_ROOT/interfacer/test/http-server/debug.log after_success: - $REPO_ROOT/contrib/release_if_new_version.sh diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index cee6ee14..6126b3aa 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -166,20 +166,18 @@ func goIntoWaitMode() { func updateLinkHintDisplay() { linkHintsToRects = make(map[string]*hintRect) - lh := len(linkHintRects) var ht string // List of closures var fc []*func() + hintStrings := buildHintStrings(len(linkHintRects)) + for i, r := range linkHintRects { // When the number of link hints is small enough // using just one key for individual link hints suffices. // Otherwise use the prepared link hint key combinations. - if lh <= len(linkHintKeys) { - ht = string(linkHintKeys[i]) - } else { - ht = linkHints[i] - } + ht = hintStrings[i] + // Add the key combination ht to the linkHintsToRects map. // When the user presses it, we can easily lookup the // link hint properties associated with it. @@ -216,6 +214,27 @@ func updateLinkHintDisplay() { linkHintWriteStringCalls = &ff } +// Builds the provided number of hint links. +// Based on https://github.com/philc/vimium/blob/881a6fdc3644f55fc02ad56454203f654cc76618/content_scripts/link_hints.coffee#L449 +func buildHintStrings(numHints int) []string { + if numHints == 0 { + return make([]string, 0) + } + + hints := make([]string, 1) + hints[0] = "" + offset := 0 + for len(hints)-offset <= numHints { + hint := hints[offset] + offset = offset + 1 + for _, char := range linkHintKeys { + hints = append(hints, string(char)+hint) + } + } + + return hints[1 : numHints+1] +} + func eraseLinkHints() { linkText = "" linkHintWriteStringCalls = nil @@ -257,7 +276,6 @@ func keyEventToString(ev *tcell.EventKey) string { return r } - func getNLastKeyEvent(n int) *tcell.EventKey { if n < 0 || keyEvents == nil { return nil diff --git a/interfacer/test/sites/links.html b/interfacer/test/sites/links.html new file mode 100644 index 00000000..106e62ba --- /dev/null +++ b/interfacer/test/sites/links.html @@ -0,0 +1,32 @@ + + + + Links + + +

Links

+ Link 1 + Link 2 + Link 3 + Link 4 + Link 5 + + Link 6 + Link 7 + Link 8 + Link 9 + Link 10 + + Link 11 + Link 12 + Link 13 + Link 14 + Link 15 + + Link 16 + Link 17 + Link 18 + Link 19 + Link 20 + + diff --git a/interfacer/test/tty/matchers.go b/interfacer/test/tty/matchers.go index 2820846b..9eb4e7d3 100644 --- a/interfacer/test/tty/matchers.go +++ b/interfacer/test/tty/matchers.go @@ -31,8 +31,7 @@ func (matcher *textInFrameMatcher) Match(actual interface{}) (success bool, err if matcher.found == text { return true, nil } - fmt.Errorf("try...") - time.Sleep(500 * time.Millisecond) + time.Sleep(100 * time.Millisecond) } return false, fmt.Errorf("Timeout. Expected\n\t%#v\nto be in the Browsh frame, but found\n\t%#v", text, matcher.found) } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 7c15f92f..f8566595 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -121,7 +121,7 @@ func WaitForText(text string, x, y int) { } time.Sleep(100 * time.Millisecond) } - panic("Waiting for '" + text + "' to appear but it didn't") + browsh.Log("Waiting for '" + text + "' to appear but it didn't") } // WaitForPageLoad waits for the page to load @@ -142,7 +142,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { } time.Sleep(50 * time.Millisecond) } - panic("Page didn't load within timeout") + browsh.Log("Page didn't load within timeout") } // GotoURL sends the browsh browser to the specified URL @@ -152,9 +152,6 @@ func GotoURL(url string) { Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() - // Hack to force text to be rerendered. Because there's a bug where text sometimes doesn't get - // rendered. - mouseClick(3, 3) // TODO: Looking for the URL isn't optimal because it could be the same URL // as the previous test. gomega.Expect(url).To(BeInFrameAt(0, 1)) @@ -166,6 +163,16 @@ func GotoURL(url string) { time.Sleep(500 * time.Millisecond) } +func MouseClick() { + // TODO: hack to work around bug where text sometimes doesn't render on page load. + // Clicking with the mouse triggers a reparse by the web extension + time.Sleep(100 * time.Millisecond) + mouseClick(3, 6) + time.Sleep(500 * time.Millisecond) + mouseClick(3, 6) + time.Sleep(500 * time.Millisecond) +} + func mouseClick(x, y int) { simScreen.InjectMouse(x, y, 1, tcell.ModNone) simScreen.InjectMouse(x, y, 0, tcell.ModNone) @@ -220,7 +227,7 @@ func GetBgColour(x, y int) [3]int32 { } func ensureOnlyOneTab() { - for len(browsh.Tabs) > 1 { + if len(browsh.Tabs) > 1 { SpecialKey(tcell.KeyCtrlW) } } @@ -241,7 +248,7 @@ func stopFirefox() { browsh.Log("Attempting to kill all firefox processes") browsh.IsConnectedToWebExtension = false browsh.Shell(rootDir + "/webext/contrib/firefoxheadless.sh kill") - time.Sleep(1500 * time.Millisecond) + time.Sleep(500 * time.Millisecond) } func runeCount(text string) int { diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index 21307f63..92a35175 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -3,18 +3,19 @@ package test import ( "browsh/interfacer/src/browsh" "testing" + "time" "github.com/gdamore/tcell" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) -func TestMain(t *testing.T) { +func TestIntegration(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Integration tests") } -var _ = Describe("Core functionality", func() { +var _ = Describe("Showing a basic webpage", func() { BeforeEach(func() { GotoURL(testSiteURL + "/smorgasbord/") }) @@ -30,8 +31,20 @@ var _ = Describe("Core functionality", func() { It("should navigate to a new page by using a link hint", func() { Expect("Another▄page").To(BeInFrameAt(12, 18)) Keyboard("f") + time.Sleep(500 * time.Millisecond) Keyboard("a") + time.Sleep(500 * time.Millisecond) Expect("Another").To(BeInFrameAt(0, 0)) + SpecialKey(tcell.KeyCtrlL) + Keyboard(testSiteURL + "/links.html") + SpecialKey(tcell.KeyEnter) + Expect("Links").To(BeInFrameAt(0, 0)) + Keyboard("f") + time.Sleep(500 * time.Millisecond) + Keyboard("a") + time.Sleep(500 * time.Millisecond) + Expect("Another").To(BeInFrameAt(0, 0)) + // TODO: test double keys }) It("should scroll the page by one line", func() { @@ -145,8 +158,8 @@ var _ = Describe("Core functionality", func() { }) It("should create a new tab", func() { - Expect("New Tab").To(BeInFrameAt(21, 0)) SpecialKey(tcell.KeyCtrlT) + Expect("New Tab").To(BeInFrameAt(21, 0)) Expect(len(browsh.Tabs)).To(Equal(2)) // need this to make tcell to work for the next round SpecialKey(tcell.KeyCtrlL) @@ -154,27 +167,35 @@ var _ = Describe("Core functionality", func() { It("should be able to goto a new URL", func() { SpecialKey(tcell.KeyCtrlT) - GotoURL(testSiteURL + "/smorgasbord/another.html") - Expect("Another▄webpage").To(BeInFrameAt(1, 3)) + Keyboard(testSiteURL + "/smorgasbord/another.html") + SpecialKey(tcell.KeyEnter) + Expect("Another").To(BeInFrameAt(21, 0)) }) It("should cycle to the next tab", func() { - GotoURL(testSiteURL + "/smorgasbord/") SpecialKey(tcell.KeyCtrlT) - GotoURL(testSiteURL + "/smorgasbord/another.html") + Expect(" ").To(BeInFrameAt(0, 1)) + // SpecialKey(tcell.KeyCtrlL) stops working after ctrl-t + Keyboard(testSiteURL + "/smorgasbord/another.html") + SpecialKey(tcell.KeyEnter) + Expect("Another").To(BeInFrameAt(21, 0)) triggerUserKeyFor("tty.keys.next-tab") - Expect("Smörgåsbord").To(BeInFrameAt(0, 0)) + URL := testSiteURL + "/smorgasbord/ " + Expect(URL).To(BeInFrameAt(0, 1)) }) It("should create a new tab", func() { Keyboard("t") Expect("New Tab").To(BeInFrameAt(21, 0)) + // need this to make tcell to work for the next round + SpecialKey(tcell.KeyCtrlL) }) It("should cycle to the next tab", func() { - GotoURL(testSiteURL + "/smorgasbord/") Keyboard("t") - GotoURL(testSiteURL + "/smorgasbord/another.html") + Keyboard(testSiteURL + "/smorgasbord/another.html") + SpecialKey(tcell.KeyEnter) + Expect("Another").To(BeInFrameAt(21, 0)) Keyboard("J") URL := testSiteURL + "/smorgasbord/ " Expect(URL).To(BeInFrameAt(0, 1)) From fdf57cd4e33e055af6610e61cfb597d528128ac5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 23 Jun 2019 21:44:49 -0700 Subject: [PATCH 46/77] changed by prettier --- webext/src/background/dimensions.js | 4 +++- webext/src/background/manager.js | 8 ++++++-- webext/src/background/tab.js | 5 +++-- webext/src/dom/dimensions.js | 4 +++- webext/src/dom/manager.js | 10 +++++----- webext/src/dom/serialise_mixin.js | 4 +++- 6 files changed, 23 insertions(+), 12 deletions(-) diff --git a/webext/src/background/dimensions.js b/webext/src/background/dimensions.js index 8cbed9ab..b8d5df35 100644 --- a/webext/src/background/dimensions.js +++ b/webext/src/background/dimensions.js @@ -30,7 +30,9 @@ export default class extends utils.mixins(CommonMixin) { ) { this.log( `Requesting browser resize for new char dimensions: ` + - `${incoming.width}x${incoming.height} (old: ${this.char.width}x${this.char.height})` + `${incoming.width}x${incoming.height} (old: ${this.char.width}x${ + this.char.height + })` ); this.char = _.clone(incoming); this.resizeBrowserWindow(); diff --git a/webext/src/background/manager.js b/webext/src/background/manager.js index 97049aa6..3a40b752 100644 --- a/webext/src/background/manager.js +++ b/webext/src/background/manager.js @@ -142,7 +142,9 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { // until it knows its internally represented ID. _newTabHandler(_request, sender, sendResponse) { this.log( - `Tab ${sender.tab.id} (${sender.tab.title}) registered with background process` + `Tab ${sender.tab.id} (${ + sender.tab.title + }) registered with background process` ); // Send the tab back to itself, such that it can be enlightened unto its own nature sendResponse(sender.tab); @@ -274,7 +276,9 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { } if (this.currentTab().channel === undefined) { this.log( - `Active tab ${this.active_tab_id} does not have a channel, so not requesting a frame` + `Active tab ${ + this.active_tab_id + } does not have a channel, so not requesting a frame` ); return false; } diff --git a/webext/src/background/tab.js b/webext/src/background/tab.js index 7f0dc785..5c1a2a99 100644 --- a/webext/src/background/tab.js +++ b/webext/src/background/tab.js @@ -105,8 +105,9 @@ export default class extends utils.mixins(CommonMixin, TabCommandsMixin) { } if (native_status === "complete" && !this._isConnected()) { this.log( - `Automatically reloading tab ${this.id} that has loaded but not connected ` + - "to the webextension" + `Automatically reloading tab ${ + this.id + } that has loaded but not connected ` + "to the webextension" ); this.reload(); this._reload_count++; diff --git a/webext/src/dom/dimensions.js b/webext/src/dom/dimensions.js index 6b482350..fa1b2d22 100644 --- a/webext/src/dom/dimensions.js +++ b/webext/src/dom/dimensions.js @@ -175,7 +175,9 @@ export default class extends utils.mixins(CommonMixin) { dom_rect.height != this._pre_calculated_char.height ) { this.log( - `Using char dims ${this._pre_calculated_char.width}x${this._pre_calculated_char.height}` + `Using char dims ${this._pre_calculated_char.width}x${ + this._pre_calculated_char.height + }` ); this.log(`Actual char dims ${dom_rect.width}x${dom_rect.height}`); } diff --git a/webext/src/dom/manager.js b/webext/src/dom/manager.js index 7859ae0e..da6cce69 100644 --- a/webext/src/dom/manager.js +++ b/webext/src/dom/manager.js @@ -214,17 +214,17 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { mutations.forEach(mutation => { if (!target) { const nodes = Array.from(mutation.addedNodes); - for(let node of nodes) { - if(node.matches && node.matches("body")) { + for (let node of nodes) { + if (node.matches && node.matches("body")) { target = node; observer.observe(target, { subtree: true, characterData: true, - childList: true, - }); + childList: true + }); break; } - } + } } this.log("!!MUTATION!!", mutation); this._debouncedSmallTextFrame(); diff --git a/webext/src/dom/serialise_mixin.js b/webext/src/dom/serialise_mixin.js index da00fe28..f8190ffc 100644 --- a/webext/src/dom/serialise_mixin.js +++ b/webext/src/dom/serialise_mixin.js @@ -179,7 +179,9 @@ export default MixinBase => _getFavicon() { let el = document.querySelector("link[rel*='icon']"); if (el) { - return ``; + return ``; } else { return ""; } From bfdc1d1e2e87f6ba7f8d50e376a2eeca69851d33 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 23 Jun 2019 22:01:38 -0700 Subject: [PATCH 47/77] get another version of prettier --- webext/src/dom/manager.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/webext/src/dom/manager.js b/webext/src/dom/manager.js index 7859ae0e..da6cce69 100644 --- a/webext/src/dom/manager.js +++ b/webext/src/dom/manager.js @@ -214,17 +214,17 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { mutations.forEach(mutation => { if (!target) { const nodes = Array.from(mutation.addedNodes); - for(let node of nodes) { - if(node.matches && node.matches("body")) { + for (let node of nodes) { + if (node.matches && node.matches("body")) { target = node; observer.observe(target, { subtree: true, characterData: true, - childList: true, - }); + childList: true + }); break; } - } + } } this.log("!!MUTATION!!", mutation); this._debouncedSmallTextFrame(); From 2206efba0a7a8bb7b484d9f9748ddacec525d0aa Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 23 Jun 2019 22:04:07 -0700 Subject: [PATCH 48/77] get another version of prettier --- webext/src/background/dimensions.js | 4 +--- webext/src/background/manager.js | 8 ++------ webext/src/background/tab.js | 5 ++--- webext/src/dom/dimensions.js | 4 +--- webext/src/dom/serialise_mixin.js | 4 +--- 5 files changed, 7 insertions(+), 18 deletions(-) diff --git a/webext/src/background/dimensions.js b/webext/src/background/dimensions.js index b8d5df35..8cbed9ab 100644 --- a/webext/src/background/dimensions.js +++ b/webext/src/background/dimensions.js @@ -30,9 +30,7 @@ export default class extends utils.mixins(CommonMixin) { ) { this.log( `Requesting browser resize for new char dimensions: ` + - `${incoming.width}x${incoming.height} (old: ${this.char.width}x${ - this.char.height - })` + `${incoming.width}x${incoming.height} (old: ${this.char.width}x${this.char.height})` ); this.char = _.clone(incoming); this.resizeBrowserWindow(); diff --git a/webext/src/background/manager.js b/webext/src/background/manager.js index 3a40b752..97049aa6 100644 --- a/webext/src/background/manager.js +++ b/webext/src/background/manager.js @@ -142,9 +142,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { // until it knows its internally represented ID. _newTabHandler(_request, sender, sendResponse) { this.log( - `Tab ${sender.tab.id} (${ - sender.tab.title - }) registered with background process` + `Tab ${sender.tab.id} (${sender.tab.title}) registered with background process` ); // Send the tab back to itself, such that it can be enlightened unto its own nature sendResponse(sender.tab); @@ -276,9 +274,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { } if (this.currentTab().channel === undefined) { this.log( - `Active tab ${ - this.active_tab_id - } does not have a channel, so not requesting a frame` + `Active tab ${this.active_tab_id} does not have a channel, so not requesting a frame` ); return false; } diff --git a/webext/src/background/tab.js b/webext/src/background/tab.js index 5c1a2a99..7f0dc785 100644 --- a/webext/src/background/tab.js +++ b/webext/src/background/tab.js @@ -105,9 +105,8 @@ export default class extends utils.mixins(CommonMixin, TabCommandsMixin) { } if (native_status === "complete" && !this._isConnected()) { this.log( - `Automatically reloading tab ${ - this.id - } that has loaded but not connected ` + "to the webextension" + `Automatically reloading tab ${this.id} that has loaded but not connected ` + + "to the webextension" ); this.reload(); this._reload_count++; diff --git a/webext/src/dom/dimensions.js b/webext/src/dom/dimensions.js index fa1b2d22..6b482350 100644 --- a/webext/src/dom/dimensions.js +++ b/webext/src/dom/dimensions.js @@ -175,9 +175,7 @@ export default class extends utils.mixins(CommonMixin) { dom_rect.height != this._pre_calculated_char.height ) { this.log( - `Using char dims ${this._pre_calculated_char.width}x${ - this._pre_calculated_char.height - }` + `Using char dims ${this._pre_calculated_char.width}x${this._pre_calculated_char.height}` ); this.log(`Actual char dims ${dom_rect.width}x${dom_rect.height}`); } diff --git a/webext/src/dom/serialise_mixin.js b/webext/src/dom/serialise_mixin.js index f8190ffc..da00fe28 100644 --- a/webext/src/dom/serialise_mixin.js +++ b/webext/src/dom/serialise_mixin.js @@ -179,9 +179,7 @@ export default MixinBase => _getFavicon() { let el = document.querySelector("link[rel*='icon']"); if (el) { - return ``; + return ``; } else { return ""; } From ee1291b41a528e0430efb19d2eaa05875b34b75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:45:01 -0400 Subject: [PATCH 49/77] Added new vimium.js with code taken from vimium --- webext/src/vimium.js | 825 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 825 insertions(+) create mode 100644 webext/src/vimium.js diff --git a/webext/src/vimium.js b/webext/src/vimium.js new file mode 100644 index 00000000..6463294f --- /dev/null +++ b/webext/src/vimium.js @@ -0,0 +1,825 @@ +// The code in this file was copied and adapted from vimium's codebase. +// It's MIT licensed, so there should be no problem mixing it with +// Browsh's codebase or even relicensing the code as LGPLv2. +// +// Here is the list of changes made to vimium's original code in order +// to better fullfill the needs of Browsh: +// +// In line 11 of getLocalHints the following code was added: +// +// if (requireHref && element.href && visibleElement.length > 0) { +// visibleElement[0]["href"] = element.href; +// } +// +// Also in getLocalHints the following outcommented code was replaced +// with the code preceding it in order to prevent Firefox from crashing: +// +// try { +// var testextend = extend(visibleElement, { rect: rects[0] }); +// nonOverlappingElements.push(testextend); +// } catch(error) { +// nonOverlappingElements.push(visibleElement); +// } +// /*nonOverlappingElements.push(extend(visibleElement, { +// rect: rects[0] +// }));*/ +// +// The following lines just before the return statement in getLocalHints were +// commented out, because we're currently not using this functionality and the +// settings for it simply don't exist in Browsh (yet). +// +// /*if (Settings.get("filterLinkHints")) { +// for (m = 0, len3 = localHints.length; m < len3; m++) { +// hint = localHints[m]; +// extend(hint, this.generateLinkText(hint)); +// } +// }*/ +// +// In multiple places Utils.isFirefox() calls were commented out or replaced with true, +// because Browsh is currently assuming that it is being run in Firefox. + +var Rect = { + create: function(x1, y1, x2, y2) { + return { + bottom: y2, + top: y1, + left: x1, + right: x2, + width: x2 - x1, + height: y2 - y1 + }; + }, + copy: function(rect) { + return { + bottom: rect.bottom, + top: rect.top, + left: rect.left, + right: rect.right, + width: rect.width, + height: rect.height + }; + }, + translate: function(rect, x, y) { + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + return { + bottom: rect.bottom + y, + top: rect.top + y, + left: rect.left + x, + right: rect.right + x, + width: rect.width, + height: rect.height + }; + }, + subtract: function(rect1, rect2) { + var rects; + rect2 = this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); + if (rect2.width < 0 || rect2.height < 0) { + return [Rect.copy(rect1)]; + } + rects = [this.create(rect1.left, rect1.top, rect2.left, rect2.top), this.create(rect2.left, rect1.top, rect2.right, rect2.top), this.create(rect2.right, rect1.top, rect1.right, rect2.top), this.create(rect1.left, rect2.top, rect2.left, rect2.bottom), this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom)]; + return rects.filter(function(rect) { + return rect.height > 0 && rect.width > 0; + }); + }, + intersects: function(rect1, rect2) { + return rect1.right > rect2.left && rect1.left < rect2.right && rect1.bottom > rect2.top && rect1.top < rect2.bottom; + }, + intersectsStrict: function(rect1, rect2) { + return rect1.right >= rect2.left && rect1.left <= rect2.right && rect1.bottom >= rect2.top && rect1.top <= rect2.bottom; + }, + equals: function(rect1, rect2) { + var i, len, property, ref; + ref = ["top", "bottom", "left", "right", "width", "height"]; + for (i = 0, len = ref.length; i < len; i++) { + property = ref[i]; + if (rect1[property] !== rect2[property]) { + return false; + } + } + return true; + }, + intersect: function(rect1, rect2) { + return this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); + } + }; + +var DomUtils = { + documentReady: function() { + var callbacks, isReady, onDOMContentLoaded, ref; + ref = [document.readyState !== "loading", []], isReady = ref[0], callbacks = ref[1]; + if (!isReady) { + window.addEventListener("DOMContentLoaded", onDOMContentLoaded = forTrusted(function() { + var callback, i, len; + window.removeEventListener("DOMContentLoaded", onDOMContentLoaded); + isReady = true; + for (i = 0, len = callbacks.length; i < len; i++) { + callback = callbacks[i]; + callback(); + } + return callbacks = null; + })); + } + return function(callback) { + if (isReady) { + return callback(); + } else { + return callbacks.push(callback); + } + }; + }, + getVisibleClientRect: function(element, testChildren) { + var child, childClientRect, clientRect, clientRects, computedStyle, i, isInlineZeroHeight, j, len, len1, ref, ref1; + if (testChildren == null) { + testChildren = false; + } + clientRects = (function() { + var i, len, ref, results; + ref = element.getClientRects(); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + clientRect = ref[i]; + results.push(Rect.copy(clientRect)); + } + return results; + })(); + isInlineZeroHeight = function() { + var elementComputedStyle, isInlineZeroFontSize; + elementComputedStyle = window.getComputedStyle(element, null); + isInlineZeroFontSize = (0 === elementComputedStyle.getPropertyValue("display").indexOf("inline")) && (elementComputedStyle.getPropertyValue("font-size") === "0px"); + isInlineZeroHeight = function() { + return isInlineZeroFontSize; + }; + return isInlineZeroFontSize; + }; + for (i = 0, len = clientRects.length; i < len; i++) { + clientRect = clientRects[i]; + if ((clientRect.width === 0 || clientRect.height === 0) && testChildren) { + ref = element.children; + for (j = 0, len1 = ref.length; j < len1; j++) { + child = ref[j]; + computedStyle = window.getComputedStyle(child, null); + if (computedStyle.getPropertyValue("float") === "none" && !((ref1 = computedStyle.getPropertyValue("position")) === "absolute" || ref1 === "fixed") && !(clientRect.height === 0 && isInlineZeroHeight() && 0 === computedStyle.getPropertyValue("display").indexOf("inline"))) { + continue; + } + childClientRect = this.getVisibleClientRect(child, true); + if (childClientRect === null || childClientRect.width < 3 || childClientRect.height < 3) { + continue; + } + return childClientRect; + } + } else { + clientRect = this.cropRectToVisible(clientRect); + if (clientRect === null || clientRect.width < 3 || clientRect.height < 3) { + continue; + } + computedStyle = window.getComputedStyle(element, null); + if (computedStyle.getPropertyValue('visibility') !== 'visible') { + continue; + } + return clientRect; + } + } + return null; + }, + cropRectToVisible: function(rect) { + var boundedRect; + boundedRect = Rect.create(Math.max(rect.left, 0), Math.max(rect.top, 0), rect.right, rect.bottom); + if (boundedRect.top >= window.innerHeight - 4 || boundedRect.left >= window.innerWidth - 4) { + return null; + } else { + return boundedRect; + } + }, + getClientRectsForAreas: function(imgClientRect, areas) { + var area, coords, diff, i, len, r, rect, rects, ref, shape, x, x1, x2, y, y1, y2; + rects = []; + for (i = 0, len = areas.length; i < len; i++) { + area = areas[i]; + coords = area.coords.split(",").map(function(coord) { + return parseInt(coord, 10); + }); + shape = area.shape.toLowerCase(); + if (shape === "rect" || shape === "rectangle") { + x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; + } else if (shape === "circle" || shape === "circ") { + x = coords[0], y = coords[1], r = coords[2]; + diff = r / Math.sqrt(2); + x1 = x - diff; + x2 = x + diff; + y1 = y - diff; + y2 = y + diff; + } else if (shape === "default") { + ref = [0, 0, imgClientRect.width, imgClientRect.height], x1 = ref[0], y1 = ref[1], x2 = ref[2], y2 = ref[3]; + } else { + x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; + } + rect = Rect.translate(Rect.create(x1, y1, x2, y2), imgClientRect.left, imgClientRect.top); + rect = this.cropRectToVisible(rect); + if (rect && !isNaN(rect.top)) { + rects.push({ + element: area, + rect: rect + }); + } + } + return rects; + }, + isSelectable: function(element) { + var unselectableTypes; + if (!(element instanceof Element)) { + return false; + } + unselectableTypes = ["button", "checkbox", "color", "file", "hidden", "image", "radio", "reset", "submit"]; + return (element.nodeName.toLowerCase() === "input" && unselectableTypes.indexOf(element.type) === -1) || element.nodeName.toLowerCase() === "textarea" || element.isContentEditable; + }, + getViewportTopLeft: function() { + var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; + box = document.documentElement; + style = getComputedStyle(box); + rect = box.getBoundingClientRect(); + if (style.position === "static" && !/content|paint|strict/.test(style.contain || "")) { + marginTop = parseInt(style.marginTop); + marginLeft = parseInt(style.marginLeft); + return { + top: -rect.top + marginTop, + left: -rect.left + marginLeft + }; + } else { + //if (Utils.isFirefox()) + if (true) { + clientTop = parseInt(style.borderTopWidth); + clientLeft = parseInt(style.borderLeftWidth); + } else { + clientTop = box.clientTop, clientLeft = box.clientLeft; + } + return { + top: -rect.top - clientTop, + left: -rect.left - clientLeft + }; + } + }, + makeXPath: function(elementArray) { + var element, i, len, xpath; + xpath = []; + for (i = 0, len = elementArray.length; i < len; i++) { + element = elementArray[i]; + xpath.push(".//" + element, ".//xhtml:" + element); + } + return xpath.join(" | "); + }, + evaluateXPath: function(xpath, resultType) { + var contextNode, namespaceResolver; + contextNode = document.webkitIsFullScreen ? document.webkitFullscreenElement : document.documentElement; + namespaceResolver = function(namespace) { + if (namespace === "xhtml") { + return "http://www.w3.org/1999/xhtml"; + } else { + return null; + } + }; + return document.evaluate(xpath, contextNode, namespaceResolver, resultType, null); + }, + simulateClick: function(element, modifiers) { + var defaultActionShouldTrigger, event, eventSequence, i, len, results; + if (modifiers == null) { + modifiers = {}; + } + eventSequence = ["mouseover", "mousedown", "mouseup", "click"]; + results = []; + for (i = 0, len = eventSequence.length; i < len; i++) { + event = eventSequence[i]; + defaultActionShouldTrigger = /*Utils.isFirefox() &&*/ Object.keys(modifiers).length === 0 && event === "click" && element.target === "_blank" && element.href && !element.hasAttribute("onclick") && !element.hasAttribute("_vimium-has-onclick-listener") ? true : this.simulateMouseEvent(event, element, modifiers); + if (event === "click" && defaultActionShouldTrigger /*&& Utils.isFirefox()*/) { + if (0 < Object.keys(modifiers).length || element.target === "_blank") { + DomUtils.simulateClickDefaultAction(element, modifiers); + } + } + results.push(defaultActionShouldTrigger); + } + return results; + }, + simulateMouseEvent: (function() { + var lastHoveredElement; + lastHoveredElement = void 0; + return function(event, element, modifiers) { + var mouseEvent; + if (modifiers == null) { + modifiers = {}; + } + if (event === "mouseout") { + if (element == null) { + element = lastHoveredElement; + } + lastHoveredElement = void 0; + if (element == null) { + return; + } + } else if (event === "mouseover") { + this.simulateMouseEvent("mouseout", void 0, modifiers); + lastHoveredElement = element; + } + mouseEvent = document.createEvent("MouseEvents"); + mouseEvent.initMouseEvent(event, true, true, window, 1, 0, 0, 0, 0, modifiers.ctrlKey, modifiers.altKey, modifiers.shiftKey, modifiers.metaKey, 0, null); + return element.dispatchEvent(mouseEvent); + }; + })(), + simulateClickDefaultAction: function(element, modifiers) { + var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; + if (modifiers == null) { + modifiers = {}; + } + if (!(((ref = element.tagName) != null ? ref.toLowerCase() : void 0) === "a" && (element.href != null))) { + return; + } + ctrlKey = modifiers.ctrlKey, shiftKey = modifiers.shiftKey, metaKey = modifiers.metaKey, altKey = modifiers.altKey; + if (KeyboardUtils.platform === "Mac") { + newTabModifier = metaKey === true && ctrlKey === false; + } else { + newTabModifier = metaKey === false && ctrlKey === true; + } + if (newTabModifier) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: shiftKey === true + }); + } else if (shiftKey === true && metaKey === false && ctrlKey === false && altKey === false) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewWindow", + url: element.href + }); + } else if (element.target === "_blank") { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: true + }); + } + } +} + + var LocalHints = { + getVisibleClickable: function(element) { + var actionName, areas, areasAndRects, base1, clientRect, contentEditable, eventType, i, imgClientRects, isClickable, jsactionRule, jsactionRules, len, map, mapName, namespace, onlyHasTabIndex, possibleFalsePositive, reason, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, role, ruleSplit, tabIndex, tabIndexValue, tagName, visibleElements, slice; + tagName = (ref = typeof (base1 = element.tagName).toLowerCase === "function" ? base1.toLowerCase() : void 0) != null ? ref : ""; + isClickable = false; + onlyHasTabIndex = false; + possibleFalsePositive = false; + visibleElements = []; + reason = null; + slice = [].slice; + if (tagName === "img") { + mapName = element.getAttribute("usemap"); + if (mapName) { + imgClientRects = element.getClientRects(); + mapName = mapName.replace(/^#/, "").replace("\"", "\\\""); + map = document.querySelector("map[name=\"" + mapName + "\"]"); + if (map && imgClientRects.length > 0) { + areas = map.getElementsByTagName("area"); + areasAndRects = DomUtils.getClientRectsForAreas(imgClientRects[0], areas); + visibleElements.push.apply(visibleElements, areasAndRects); + } + } + } + if (((ref1 = (ref2 = element.getAttribute("aria-hidden")) != null ? ref2.toLowerCase() : void 0) === "" || ref1 === "true") || ((ref3 = (ref4 = element.getAttribute("aria-disabled")) != null ? ref4.toLowerCase() : void 0) === "" || ref3 === "true")) { + return []; + } + if (this.checkForAngularJs == null) { + this.checkForAngularJs = (function() { + var angularElements, i, k, len, len1, ngAttributes, prefix, ref5, ref6, separator; + angularElements = document.getElementsByClassName("ng-scope"); + if (angularElements.length === 0) { + return function() { + return false; + }; + } else { + ngAttributes = []; + ref5 = ['', 'data-', 'x-']; + for (i = 0, len = ref5.length; i < len; i++) { + prefix = ref5[i]; + ref6 = ['-', ':', '_']; + for (k = 0, len1 = ref6.length; k < len1; k++) { + separator = ref6[k]; + ngAttributes.push(prefix + "ng" + separator + "click"); + } + } + return function(element) { + var attribute, l, len2; + for (l = 0, len2 = ngAttributes.length; l < len2; l++) { + attribute = ngAttributes[l]; + if (element.hasAttribute(attribute)) { + return true; + } + } + return false; + }; + } + })(); + } + isClickable || (isClickable = this.checkForAngularJs(element)); + if (element.hasAttribute("onclick") || (role = element.getAttribute("role")) && ((ref5 = role.toLowerCase()) === "button" || ref5 === "tab" || ref5 === "link" || ref5 === "checkbox" || ref5 === "menuitem" || ref5 === "menuitemcheckbox" || ref5 === "menuitemradio") || (contentEditable = element.getAttribute("contentEditable")) && ((ref6 = contentEditable.toLowerCase()) === "" || ref6 === "contenteditable" || ref6 === "true")) { + isClickable = true; + } + if (!isClickable && element.hasAttribute("jsaction")) { + jsactionRules = element.getAttribute("jsaction").split(";"); + for (i = 0, len = jsactionRules.length; i < len; i++) { + jsactionRule = jsactionRules[i]; + ruleSplit = jsactionRule.trim().split(":"); + if ((1 <= (ref7 = ruleSplit.length) && ref7 <= 2)) { + ref8 = ruleSplit.length === 1 ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), ["_"]) : [ruleSplit[0]].concat(slice.call(ruleSplit[1].trim().split(".")), ["_"]), eventType = ref8[0], namespace = ref8[1], actionName = ref8[2]; + isClickable || (isClickable = eventType === "click" && namespace !== "none" && actionName !== "_"); + } + } + } + switch (tagName) { + case "a": + isClickable = true; + break; + case "textarea": + isClickable || (isClickable = !element.disabled && !element.readOnly); + break; + case "input": + isClickable || (isClickable = !(((ref9 = element.getAttribute("type")) != null ? ref9.toLowerCase() : void 0) === "hidden" || element.disabled || (element.readOnly && DomUtils.isSelectable(element)))); + break; + case "button": + case "select": + isClickable || (isClickable = !element.disabled); + break; + case "label": + isClickable || (isClickable = (element.control != null) && !element.control.disabled && (this.getVisibleClickable(element.control)).length === 0); + break; + case "body": + isClickable || (isClickable = element === document.body && !windowIsFocused() && window.innerWidth > 3 && window.innerHeight > 3 && ((ref10 = document.body) != null ? ref10.tagName.toLowerCase() : void 0) !== "frameset" ? reason = "Frame." : void 0); + //isClickable || (isClickable = element === document.body && windowIsFocused() && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "img": + isClickable || (isClickable = (ref11 = element.style.cursor) === "zoom-in" || ref11 === "zoom-out"); + break; + case "div": + case "ol": + case "ul": + //isClickable || (isClickable = element.clientHeight < element.scrollHeight && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "details": + isClickable = true; + reason = "Open."; + } + if (!isClickable && 0 <= ((ref12 = element.getAttribute("class")) != null ? ref12.toLowerCase().indexOf("button") : void 0)) { + possibleFalsePositive = isClickable = true; + } + tabIndexValue = element.getAttribute("tabindex"); + tabIndex = tabIndexValue === "" ? 0 : parseInt(tabIndexValue); + if (!(isClickable || isNaN(tabIndex) || tabIndex < 0)) { + isClickable = onlyHasTabIndex = true; + } + if (isClickable) { + clientRect = DomUtils.getVisibleClientRect(element, true); + if (clientRect !== null) { + visibleElements.push({ + element: element, + rect: clientRect, + secondClassCitizen: onlyHasTabIndex, + possibleFalsePositive: possibleFalsePositive, + reason: reason + }); + } + } + return visibleElements; + }, + getLocalHints: function(requireHref) { + var descendantsToCheck, element, elements, hint, i, k, l, left, len, len1, len2, len3, localHints, m, negativeRect, nonOverlappingElements, position, rects, ref, ref1, top, visibleElement, visibleElements; + if (!document.documentElement) { + return []; + } + elements = document.documentElement.getElementsByTagName("*"); + visibleElements = []; + for (i = 0, len = elements.length; i < len; i++) { + element = elements[i]; + if (!(requireHref && !element.href)) { + visibleElement = this.getVisibleClickable(element); + if (requireHref && element.href && visibleElement.length > 0) { + visibleElement[0]["href"] = element.href; + } + visibleElements.push.apply(visibleElements, visibleElement); + } + } + visibleElements = visibleElements.reverse(); + descendantsToCheck = [1, 2, 3]; + visibleElements = (function() { + var k, len1, results; + results = []; + for (position = k = 0, len1 = visibleElements.length; k < len1; position = ++k) { + element = visibleElements[position]; + if (element.possibleFalsePositive && (function() { + var _, candidateDescendant, index, l, len2; + index = Math.max(0, position - 6); + while (index < position) { + candidateDescendant = visibleElements[index].element; + for (l = 0, len2 = descendantsToCheck.length; l < len2; l++) { + _ = descendantsToCheck[l]; + candidateDescendant = candidateDescendant != null ? candidateDescendant.parentElement : void 0; + if (candidateDescendant === element.element) { + return true; + } + } + index += 1; + } + return false; + })()) { + continue; + } + results.push(element); + } + return results; + })(); + localHints = nonOverlappingElements = []; + while (visibleElement = visibleElements.pop()) { + rects = [visibleElement.rect]; + for (k = 0, len1 = visibleElements.length; k < len1; k++) { + negativeRect = visibleElements[k].rect; + rects = (ref = []).concat.apply(ref, rects.map(function(rect) { + return Rect.subtract(rect, negativeRect); + })); + } + if (rects.length > 0) { + try { + var testextend = extend(visibleElement, { rect: rects[0] }); + nonOverlappingElements.push(testextend); + } catch(error) { + nonOverlappingElements.push(visibleElement); + } + /*nonOverlappingElements.push(extend(visibleElement, { + rect: rects[0] + }));*/ + } else { + if (!visibleElement.secondClassCitizen) { + nonOverlappingElements.push(visibleElement); + } + } + } + ref1 = DomUtils.getViewportTopLeft(), top = ref1.top, left = ref1.left; + for (l = 0, len2 = nonOverlappingElements.length; l < len2; l++) { + hint = nonOverlappingElements[l]; + hint.rect.top += top; + hint.rect.left += left; + } + /*if (Settings.get("filterLinkHints")) { + for (m = 0, len3 = localHints.length; m < len3; m++) { + hint = localHints[m]; + extend(hint, this.generateLinkText(hint)); + } + }*/ + return localHints; + }, + generateLinkText: function(hint) { + var element, linkText, nodeName, ref, showLinkText; + element = hint.element; + linkText = ""; + showLinkText = false; + nodeName = element.nodeName.toLowerCase(); + if (nodeName === "input") { + if ((element.labels != null) && element.labels.length > 0) { + linkText = element.labels[0].textContent.trim(); + if (linkText[linkText.length - 1] === ":") { + linkText = linkText.slice(0, linkText.length - 1); + } + showLinkText = true; + } else if (((ref = element.getAttribute("type")) != null ? ref.toLowerCase() : void 0) === "file") { + linkText = "Choose File"; + } else if (element.type !== "password") { + linkText = element.value; + if (!linkText && 'placeholder' in element) { + linkText = element.placeholder; + } + } + } else if (nodeName === "a" && !element.textContent.trim() && element.firstElementChild && element.firstElementChild.nodeName.toLowerCase() === "img") { + linkText = element.firstElementChild.alt || element.firstElementChild.title; + if (linkText) { + showLinkText = true; + } + } else if (hint.reason != null) { + linkText = hint.reason; + showLinkText = true; + } else if (0 < element.textContent.length) { + linkText = element.textContent.slice(0, 256); + } else if (element.hasAttribute("title")) { + linkText = element.getAttribute("title"); + } else { + linkText = element.innerHTML.slice(0, 256); + } + return { + linkText: linkText.trim(), + showLinkText: showLinkText + }; + } + }; + +var VimiumNormal = { + followLink : function(linkElement) { + if (linkElement.nodeName.toLowerCase() === "link") { + return window.location.href = linkElement.href; + } else { + linkElement.scrollIntoView(); + return DomUtils.simulateClick(linkElement); + } + }, + findAndFollowLink : function(linkStrings) { + var boundingClientRect, candidateLink, candidateLinks, computedStyle, exactWordRegex, i, j, k, l, len, len1, len2, len3, link, linkMatches, linkString, links, linksXPath, m, n, ref, ref1; + linksXPath = DomUtils.makeXPath(["a", "*[@onclick or @role='link' or contains(@class, 'button')]"]); + links = DomUtils.evaluateXPath(linksXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + candidateLinks = []; + for (i = j = ref = links.snapshotLength - 1; j >= 0; i = j += -1) { + link = links.snapshotItem(i); + boundingClientRect = link.getBoundingClientRect(); + if (boundingClientRect.width === 0 || boundingClientRect.height === 0) { + continue; + } + computedStyle = window.getComputedStyle(link, null); + if (computedStyle.getPropertyValue("visibility") !== "visible" || computedStyle.getPropertyValue("display") === "none") { + continue; + } + linkMatches = false; + for (k = 0, len = linkStrings.length; k < len; k++) { + linkString = linkStrings[k]; + if (link.innerText.toLowerCase().indexOf(linkString) !== -1 || 0 <= ((ref1 = link.value) != null ? typeof ref1.indexOf === "function" ? ref1.indexOf(linkString) : void 0 : void 0)) { + linkMatches = true; + break; + } + } + if (!linkMatches) { + continue; + } + candidateLinks.push(link); + } + if (candidateLinks.length === 0) { + return; + } + for (l = 0, len1 = candidateLinks.length; l < len1; l++) { + link = candidateLinks[l]; + link.wordCount = link.innerText.trim().split(/\s+/).length; + } + candidateLinks.forEach(function(a, i) { + return a.originalIndex = i; + }); + candidateLinks = candidateLinks.sort(function(a, b) { + if (a.wordCount === b.wordCount) { + return a.originalIndex - b.originalIndex; + } else { + return a.wordCount - b.wordCount; + } + }).filter(function(a) { + return a.wordCount <= candidateLinks[0].wordCount + 1; + }); + for (m = 0, len2 = linkStrings.length; m < len2; m++) { + linkString = linkStrings[m]; + exactWordRegex = /\b/.test(linkString[0]) || /\b/.test(linkString[linkString.length - 1]) ? new RegExp("\\b" + linkString + "\\b", "i") : new RegExp(linkString, "i"); + for (n = 0, len3 = candidateLinks.length; n < len3; n++) { + candidateLink = candidateLinks[n]; + if (exactWordRegex.test(candidateLink.innerText) || (candidateLink.value && exactWordRegex.test(candidateLink.value))) { + this.followLink(candidateLink); + return true; + } + } + } + return false; + }, + findAndFollowRel : function(value) { + var element, elements, j, k, len, len1, relTags, tag; + relTags = ["link", "a", "area"]; + for (j = 0, len = relTags.length; j < len; j++) { + tag = relTags[j]; + elements = document.getElementsByTagName(tag); + for (k = 0, len1 = elements.length; k < len1; k++) { + element = elements[k]; + if (element.hasAttribute("rel") && element.rel.toLowerCase() === value) { + this.followLink(element); + return true; + } + } + } + }, + textInputXPath : function() { + var inputElements, textInputTypes; + textInputTypes = ["text", "search", "email", "url", "number", "password", "date", "tel"]; + inputElements = [ + "input[" + "(" + textInputTypes.map(function(type) { + return '@type="' + type + '"'; + }).join(" or ") + "or not(@type))" + " and not(@disabled or @readonly)]", "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" + ]; + return typeof DomUtils !== "undefined" && DomUtils !== null ? DomUtils.makeXPath(inputElements) : void 0; + }, + focusInput: function(count) { + var element, elements, hint, hints, i, recentlyFocusedElement, resultSet, selectedInputIndex, tuple, visibleInputs; + resultSet = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + visibleInputs = (function() { + var j, ref, results; + results = []; + for (i = j = 0, ref = resultSet.snapshotLength; j < ref; i = j += 1) { + element = resultSet.snapshotItem(i); + if (!DomUtils.getVisibleClientRect(element, true)) { + continue; + } + results.push({ + element: element, + index: i, + rect: Rect.copy(element.getBoundingClientRect()) + }); + } + return results; + })(); + visibleInputs.sort(function(arg, arg1) { + var element1, element2, i1, i2, tabDifference; + element1 = arg.element, i1 = arg.index; + element2 = arg1.element, i2 = arg1.index; + if (element1.tabIndex > 0) { + if (element2.tabIndex > 0) { + tabDifference = element1.tabIndex - element2.tabIndex; + if (tabDifference !== 0) { + return tabDifference; + } else { + return i1 - i2; + } + } else { + return -1; + } + } else if (element2.tabIndex > 0) { + return 1; + } else { + return i1 - i2; + } + }); + if (visibleInputs.length === 0) { + HUD.showForDuration("There are no inputs to focus.", 1000); + return; + } + recentlyFocusedElement = lastFocusedInput(); + selectedInputIndex = count === 1 ? (elements = visibleInputs.map(function(visibleInput) { + return visibleInput.element; + }), Math.max(0, elements.indexOf(recentlyFocusedElement))) : Math.min(count, visibleInputs.length) - 1; + hints = (function() { + var j, len, results; + results = []; + for (j = 0, len = visibleInputs.length; j < len; j++) { + tuple = visibleInputs[j]; + hint = DomUtils.createElement("div"); + hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"; + hint.style.left = (tuple.rect.left - 1) + window.scrollX + "px"; + hint.style.top = (tuple.rect.top - 1) + window.scrollY + "px"; + hint.style.width = tuple.rect.width + "px"; + hint.style.height = tuple.rect.height + "px"; + results.push(hint); + } + return results; + })(); + return new FocusSelector(hints, visibleInputs, selectedInputIndex); + } + }; + + +export function MiscVimium() { + if (window.forTrusted == null) { + window.forTrusted = function(handler) { + return function(event) { + if (event != null ? event.isTrusted : void 0) { + return handler.apply(this, arguments); + } else { + return true; + } + }; + }; + } + window.windowIsFocused = function() { + var windowHasFocus; + windowHasFocus = null; + DomUtils.documentReady(function() { + return windowHasFocus = document.hasFocus(); + }); + window.addEventListener("focus", forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = true; + } + return true; + })); + window.addEventListener("blur", forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = false; + } + return true; + })); + return function() { + return windowHasFocus; + }; + }; +}; + +export { Rect } +export { DomUtils } +export { LocalHints } +export { VimiumNormal } + + From 3b246ff79640329352cf38a7a9d746a04599401d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:52:00 -0400 Subject: [PATCH 50/77] Created overlayVimMode function for displaying vim navigation state --- interfacer/src/browsh/ui.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 973bb5de..79dd49bc 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -108,6 +108,41 @@ func urlBarFocus(on bool) { } } +func overlayVimMode() { + _, height := screen.Size() + switch vimMode { + case InsertMode: + writeString(0, height-1, "ins", tcell.StyleDefault) + case LinkMode: + writeString(0, height-1, "lnk", tcell.StyleDefault) + case LinkModeNewTab: + writeString(0, height-1, "LNK", tcell.StyleDefault) + case LinkModeCopy: + writeString(0, height-1, "cp", tcell.StyleDefault) + case VisualMode: + writeString(0, height-1, "vis", tcell.StyleDefault) + case CaretMode: + writeString(0, height-1, "car", tcell.StyleDefault) + writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) + case FindMode: + writeString(0, height-1, "/" + findText, tcell.StyleDefault) + case MakeMarkMode: + writeString(0, height-1, "mark", tcell.StyleDefault) + case GotoMarkMode: + writeString(0, height-1, "goto", tcell.StyleDefault) + } + + switch vimMode { + case LinkMode, LinkModeNewTab, LinkModeCopy: + if !linkModeWithHints { + findAndHighlightTextOnScreen(linkText) } + + if linkHintWriteStringCalls != nil { + (*linkHintWriteStringCalls)() + } + } +} + func overlayPageStatusMessage() { _, height := screen.Size() writeString(0, height-1, CurrentTab.StatusMessage, tcell.StyleDefault) From baf808f35d6dae061e533d41f7addbbba3065995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:57:43 -0400 Subject: [PATCH 51/77] gofmt code --- interfacer/src/browsh/ui.go | 61 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 79dd49bc..6e38a1ec 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -109,38 +109,39 @@ func urlBarFocus(on bool) { } func overlayVimMode() { - _, height := screen.Size() - switch vimMode { - case InsertMode: - writeString(0, height-1, "ins", tcell.StyleDefault) - case LinkMode: - writeString(0, height-1, "lnk", tcell.StyleDefault) - case LinkModeNewTab: - writeString(0, height-1, "LNK", tcell.StyleDefault) - case LinkModeCopy: - writeString(0, height-1, "cp", tcell.StyleDefault) - case VisualMode: - writeString(0, height-1, "vis", tcell.StyleDefault) - case CaretMode: - writeString(0, height-1, "car", tcell.StyleDefault) - writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) - case FindMode: - writeString(0, height-1, "/" + findText, tcell.StyleDefault) - case MakeMarkMode: - writeString(0, height-1, "mark", tcell.StyleDefault) - case GotoMarkMode: - writeString(0, height-1, "goto", tcell.StyleDefault) - } + _, height := screen.Size() + switch vimMode { + case InsertMode: + writeString(0, height-1, "ins", tcell.StyleDefault) + case LinkMode: + writeString(0, height-1, "lnk", tcell.StyleDefault) + case LinkModeNewTab: + writeString(0, height-1, "LNK", tcell.StyleDefault) + case LinkModeCopy: + writeString(0, height-1, "cp", tcell.StyleDefault) + case VisualMode: + writeString(0, height-1, "vis", tcell.StyleDefault) + case CaretMode: + writeString(0, height-1, "car", tcell.StyleDefault) + writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) + case FindMode: + writeString(0, height-1, "/"+findText, tcell.StyleDefault) + case MakeMarkMode: + writeString(0, height-1, "mark", tcell.StyleDefault) + case GotoMarkMode: + writeString(0, height-1, "goto", tcell.StyleDefault) + } - switch vimMode { - case LinkMode, LinkModeNewTab, LinkModeCopy: - if !linkModeWithHints { - findAndHighlightTextOnScreen(linkText) } + switch vimMode { + case LinkMode, LinkModeNewTab, LinkModeCopy: + if !linkModeWithHints { + findAndHighlightTextOnScreen(linkText) + } - if linkHintWriteStringCalls != nil { - (*linkHintWriteStringCalls)() - } - } + if linkHintWriteStringCalls != nil { + (*linkHintWriteStringCalls)() + } + } } func overlayPageStatusMessage() { From ca30b7722bbedc5b861b9ac9b7f4c8dd1ed38b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 14:59:09 -0400 Subject: [PATCH 52/77] added command for links hints --- interfacer/src/browsh/comms.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interfacer/src/browsh/comms.go b/interfacer/src/browsh/comms.go index 9bfada9c..c896ba65 100644 --- a/interfacer/src/browsh/comms.go +++ b/interfacer/src/browsh/comms.go @@ -88,6 +88,8 @@ func handleWebextensionCommand(message []byte) { } case "/screenshot": saveScreenshot(parts[1]) + case "/link_hints": + parseJSONLinkHints(strings.Join(parts[1:], ",")) default: Log("WEBEXT: " + string(message)) } From dc9533969f9ce7bb55273ce834e0cb95fe5c61bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 15:00:23 -0400 Subject: [PATCH 53/77] Set default DISPLAY environment variable for xclipboard functionality --- interfacer/src/browsh/browsh.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index d3f2dc5f..29f776ba 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -181,6 +181,12 @@ func ttyEntry() { // from tcell. os.Setenv("TERM", "xterm-truecolor") } + // This is for getting the clipboard (github.com/atotto/clipboard) to work + // with the applications xsel and xclip on systems with an X display server. + if os.Getenv("DISPLAY") == "" { + os.Setenv("DISPLAY", ":0") + } + realScreen, err := tcell.NewScreen() if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) From ebc8de95b90fea3869f1ddede08b956ee7e11eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Sun, 7 Oct 2018 15:10:11 -0400 Subject: [PATCH 54/77] ignore manifest.json backup file --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 539a2ec0..51c4caf8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,6 @@ interfacer/browsh webextension.go webext/node_modules webext/dist/* +webext/manifest.json~ dist *.xpi From 8fc15f3301b7f972ce44f20b571f2eb6a2f3982e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Thu, 18 Oct 2018 05:30:56 -0400 Subject: [PATCH 55/77] ignore debug log in interfacer/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 51c4caf8..59a770c8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ interfacer/vendor interfacer/dist interfacer/interfacer interfacer/browsh +interfacer/debug webextension.go webext/node_modules webext/dist/* From fac1af7f2a2e001372f1a04b7576d254d7e72793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Wed, 24 Oct 2018 13:48:54 -0400 Subject: [PATCH 56/77] Fixed typo in comment --- interfacer/src/browsh/input_box.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index b69010dc..405096e4 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -12,7 +12,7 @@ var activeInputBox *inputBox // A box into which you can enter text. Generally will be forwarded to a standard // HTML input box in the real browser. // -// Note that tcell alreay has some ready-made code in its 'views' concept for +// Note that tcell already has some ready-made code in its 'views' concept for // dealing with input areas. However, at the time of writing it wasn't well documented, // so it was unclear how easy it would be to integrate the requirements of Browsh's // input boxes - namely overlaying them onto the existing graphics and having them From c794f102877325e49fbb8b667d5f8fb5772f65a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 06:39:36 -0400 Subject: [PATCH 57/77] Added sessions permission --- webext/manifest.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/webext/manifest.json b/webext/manifest.json index bd6dc0fc..0d26d3a1 100644 --- a/webext/manifest.json +++ b/webext/manifest.json @@ -32,6 +32,7 @@ "", "webRequest", "webRequestBlocking", - "tabs" + "tabs", + "sessions" ] } From 631483bbd910b90aa72cc113b19fec6f1f96c338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 09:11:24 -0400 Subject: [PATCH 58/77] Added initial configuration for vim like keybindings. The keybindings are hardcoded for now, but this is going to change. --- interfacer/src/browsh/config.go | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go index 81f56ebc..7405f1d5 100644 --- a/interfacer/src/browsh/config.go +++ b/interfacer/src/browsh/config.go @@ -73,6 +73,51 @@ func getFirefoxProfilePath() string { func setDefaults() { // Temporary experimental configurable keybindings viper.SetDefault("tty.keys.next-tab", []string{"\u001c", "28", "2"}) + + // Vim commands + vimCommandsBindings["gg"] = "scrollToTop" + vimCommandsBindings["G"] = "scrollToBottom" + vimCommandsBindings["j"] = "scrollDown" + vimCommandsBindings["k"] = "scrollUp" + vimCommandsBindings["h"] = "scrollLeft" + vimCommandsBindings["l"] = "scrollRight" + vimCommandsBindings["d"] = "scrollHalfPageDown" + vimCommandsBindings["u"] = "scrollHalfPageUp" + vimCommandsBindings["e"] = "editURL" + vimCommandsBindings["ge"] = "editURL" + vimCommandsBindings["H"] = "historyBack" + vimCommandsBindings["L"] = "historyForward" + vimCommandsBindings["J"] = "prevTab" + vimCommandsBindings["K"] = "nextTab" + vimCommandsBindings["r"] = "reload" + vimCommandsBindings["xx"] = "removeTab" + vimCommandsBindings["X"] = "restoreTab" + vimCommandsBindings["t"] = "newTab" + vimCommandsBindings["/"] = "findMode" + vimCommandsBindings["n"] = "findNext" + vimCommandsBindings["N"] = "findPrevious" + vimCommandsBindings["g0"] = "firstTab" + vimCommandsBindings["g$"] = "lastTab" + vimCommandsBindings["gu"] = "urlUp" + vimCommandsBindings["gU"] = "urlRoot" + vimCommandsBindings["<<"] = "moveTabLeft" + vimCommandsBindings[">>"] = "moveTabRight" + vimCommandsBindings["^"] = "previouslyVisitedTab" + vimCommandsBindings["m"] = "makeMark" + vimCommandsBindings["'"] = "gotoMark" + vimCommandsBindings["i"] = "insertMode" + vimCommandsBindings["yy"] = "copyURL" + vimCommandsBindings["p"] = "openClipboardURL" + vimCommandsBindings["P"] = "openClipboardURLInNewTab" + vimCommandsBindings["gi"] = "focusFirstTextInput" + vimCommandsBindings["f"] = "openLinkInCurrentTab" + vimCommandsBindings["F"] = "openLinkInNewTab" + vimCommandsBindings["yf"] = "copyLinkURL" + vimCommandsBindings["[["] = "followLinkLabeledPrevious" + vimCommandsBindings["]]"] = "followLinkLabeledNext" + vimCommandsBindings["yt"] = "duplicateTab" + vimCommandsBindings["v"] = "visualMode" + vimCommandsBindings["?"] = "viewHelp" } func loadConfig() { From 86acac617be451fa693f0cdfe97dbd9d015e67f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 09:14:40 -0400 Subject: [PATCH 59/77] Added duplicate_tab, restore_tab commands. --- webext/src/background/tty_commands_mixin.js | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/webext/src/background/tty_commands_mixin.js b/webext/src/background/tty_commands_mixin.js index 92911b12..815674b6 100644 --- a/webext/src/background/tty_commands_mixin.js +++ b/webext/src/background/tty_commands_mixin.js @@ -29,9 +29,15 @@ export default MixinBase => case "/switch_to_tab": this.switchToTab(parts.slice(1).join(",")); break; + case "/duplicate_tab": + this.duplicateTab(parts.slice(1).join(",")); + break; case "/remove_tab": this.removeTab(parts.slice(1).join(",")); break; + case "/restore_tab": + this.restoreTab(); + break; case "/raw_text_request": this._rawTextRequest(parts[1], parts[2], parts.slice(3).join(",")); break; @@ -175,6 +181,24 @@ export default MixinBase => this.tabs[id] = null; } + duplicateTab(id) { + browser.tabs.duplicate(parseInt(id)); + } + + restoreTab() { + var sessionsInfo = browser.sessions.getRecentlyClosed({maxResults: 1 }); + sessionsInfo.then(this._restoreTab); + } + + _restoreTab(sessionsInfo) { + var mySessionInfo = sessionsInfo[0]; + if (mySessionInfo.tab) { + browser.sessions.restore(mySessionInfo.tab.sessionId); + } else { + browser.sessions.restore(mySessionInfo.window.sessionId); + } + } + // We use the `browser` object here rather than going into the actual content script // because the content script may have crashed, even never loaded. screenshotActiveTab() { From 7b7e6bc308321220d52e5c7d39890321755bade8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 09:20:52 -0400 Subject: [PATCH 60/77] Refactored code using switchToTab and added new features. Added moveTabLeft and moveTabRight functions, which take a tab ID and try to move the tab as far right or left in the tabOrder as possible. Added previouslyVisitedTab function that switches to the previously selected tab. --- interfacer/src/browsh/tab.go | 75 ++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index 19461a58..900b111b 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -18,6 +18,9 @@ var tabsOrder []int // the tab being deleted, so we need to keep track of all deleted IDs var tabsDeleted []int +// ID of the tab that was active before the current one +var previouslyVisitedTabID int + // A single tab synced from the browser type tab struct { ID int `json:"id"` @@ -61,6 +64,10 @@ func newTab(id int) { } } +func restoreTab() { + sendMessageToWebExtension("/restore_tab") +} + func removeTab(id int) { if len(Tabs) == 1 { quitBrowsh() @@ -84,6 +91,40 @@ func removeTabIDfromTabsOrder(id int) { } } +func moveTabLeft(id int) { + // If the tab ID is already completely to the left in the tab order + // there's nothing to do + if tabsOrder[0] == id { + return + } + + for i, tabId := range tabsOrder { + if tabId == id { + tabsOrder[i-1], tabsOrder[i] = tabsOrder[i], tabsOrder[i-1] + break + } + } +} + +func moveTabRight(id int) { + // If the tab ID is already completely to the right in the tab order + // there's nothing to do + if tabsOrder[len(tabsOrder)-1] == id { + return + } + + for i, tabId := range tabsOrder { + if tabId == id { + tabsOrder[i+1], tabsOrder[i] = tabsOrder[i], tabsOrder[i+1] + break + } + } +} + +func duplicateTab(id int) { + sendMessageToWebExtension(fmt.Sprintf("/duplicate_tab,%d", id)) +} + // Creating a new tab in the browser without a URI means it won't register with the // web extension, which means that, come the moment when we actually have a URI for the new // tab then we can't talk to it to tell it navigate. So we need to only create a real new @@ -116,15 +157,41 @@ func nextTab() { } else { i++ } - sendMessageToWebExtension(fmt.Sprintf("/switch_to_tab,%d", tabsOrder[i])) - CurrentTab = Tabs[tabsOrder[i]] - renderUI() - renderCurrentTabWindow() + switchToTab(tabsOrder[i]) break } } } +func prevTab() { + for i := 0; i < len(tabsOrder); i++ { + if tabsOrder[i] == CurrentTab.ID { + if i-1 < 0 { + i = len(tabsOrder) - 1 + } else { + i-- + } + switchToTab(tabsOrder[i]) + break + } + } +} + +func previouslyVisitedTab() { + if previouslyVisitedTabID == 0 { + return + } + switchToTab(previouslyVisitedTabID) +} + +func switchToTab(num int) { + sendMessageToWebExtension(fmt.Sprintf("/switch_to_tab,%d", num)) + previouslyVisitedTabID = CurrentTab.ID + CurrentTab = Tabs[num] + renderUI() + renderCurrentTabWindow() +} + func isTabPreviouslyDeleted(id int) bool { for i := 0; i < len(tabsDeleted); i++ { if tabsDeleted[i] == id { From 15c7b45b6feb37a6cfcb5b9f4cf8caa78e9e3b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:05:09 -0400 Subject: [PATCH 61/77] Added features needed for vim like navigation to the webextension. --- webext/src/background/tab_commands_mixin.js | 3 + webext/src/dom/commands_mixin.js | 189 +++++++++++++++++++- 2 files changed, 183 insertions(+), 9 deletions(-) diff --git a/webext/src/background/tab_commands_mixin.js b/webext/src/background/tab_commands_mixin.js index d26baeb9..99f66c83 100644 --- a/webext/src/background/tab_commands_mixin.js +++ b/webext/src/background/tab_commands_mixin.js @@ -17,6 +17,9 @@ export default MixinBase => case "/frame_pixels": this.sendToTerminal(`/frame_pixels,${message.slice(14)}`); break; + case "/link_hints": + this.sendToTerminal(`/link_hints,${message.slice(12)}`); + break; case "/tab_info": incoming = JSON.parse(utils.rebuildArgsToSingleArg(parts)); this._updateTabInfo(incoming); diff --git a/webext/src/dom/commands_mixin.js b/webext/src/dom/commands_mixin.js index 10b357d2..3541cac4 100644 --- a/webext/src/dom/commands_mixin.js +++ b/webext/src/dom/commands_mixin.js @@ -1,4 +1,10 @@ import utils from "utils"; +import { Rect } from "vimium"; +import { DomUtils } from "vimium"; +import { LocalHints } from "vimium"; +import { VimiumNormal } from "vimium"; +import { MiscVimium } from "vimium"; +MiscVimium(); export default MixinBase => class extends MixinBase { @@ -35,19 +41,148 @@ export default MixinBase => break; case "/url": url = utils.rebuildArgsToSingleArg(parts); - document.location.href = url; + window.location.href = url; + break; + case "/url_up": + this.urlUp(); + break; + case "/url_root": + window.location.href = window.location.origin; break; case "/history_back": history.go(-1); break; + case "/history_forward": + history.go(1); + break; + case "/reload": + window.location.reload(); + break; case "/window_stop": window.stop(); break; + case "/find_next": + this.findNext(parts[1]); + break; + case "/find_previous": + window.find(parts[1], false, true, false, false, true, true); + break; + case "/get_link_hints": + this.getLinkHints(false); + break; + case "/get_clickable_hints": + this.getLinkHints(true); + break; + case "/focus_first_text_input": + this.focusFirstTextInput(); + break; + case "/follow_link_labeled_next": + this._followLinkLabeledNext(); + break; + case "/follow_link_labeled_previous": + this._followLinkLabeledPrevious(); + break; default: this.log("Unknown command sent to tab", message); } } + focusFirstTextInput() { + VimiumNormal.focusInput(1); + } + + //adapted vimium code + followLinkLabeledNext() { + var nextPatterns = "next,more,newer,>,›,→,»,≫,>>,weiter" || ""; + var nextStrings = nextPatterns.split(",").filter(function(s) { + return s.trim().length; + }); + return ( + VimiumNormal.findAndFollowRel("next") || + VimiumNormal.findAndFollowLink(nextStrings) + ); + } + + _followLinkLabeledNext() { + this.followLinkLabeledNext(); + } + + //adapted vimium code + followLinkLabeledPrevious() { + var previousPatterns = + "prev,previous,back,older,<,‹,←,«,≪,<<,zurück" || ""; + var previousStrings = previousPatterns.split(",").filter(function(s) { + return s.trim().length; + }); + return ( + VimiumNormal.findAndFollowRel("prev") || + VimiumNormal.findAndFollowLink(previousStrings) + ); + } + + _followLinkLabeledPrevious() { + this.followLinkLabeledPrevious(); + } + + urlUp() { + // this is taken from vimium's code + var url = window.location.href; + if (url[url.length - 1] === "/") { + url = url.substring(0, url.length - 1); + } + var urlsplit = url.split("/"); + // make sure we haven't hit the base domain yet + if (urlsplit.length > 3) { + urlsplit = urlsplit.slice(0, Math.max(3, urlsplit.length - 1)); + window.location.href = urlsplit.join("/"); + } + } + + getLinkHints(clickable) { + var hints = LocalHints.getLocalHints(!clickable); + var rect, bottom, top, left, right, width, height, results, result, href; + results = []; + for (let idx in hints) { + if (!hints[idx].hasOwnProperty("rect")) { + continue; + } + href = hints[idx]["href"]; + rect = hints[idx]["rect"]; + bottom = Math.round( + ((rect["bottom"] - window.scrollY) * + this.dimensions.scale_factor.height) / + 2 + ); + top = Math.round( + ((rect["top"] - window.scrollY) * + this.dimensions.scale_factor.height) / + 2 + ); + left = Math.round(rect["left"] * this.dimensions.scale_factor.width); + right = Math.round(rect["right"] * this.dimensions.scale_factor.width); + result = Rect.create(left, top, right, bottom); + result.href = href; + results.push(result); + } + this.sendMessage(`/link_hints,${JSON.stringify(results)}`); + } + + findNext(text) { + window.find(text, false, false, false, false, true, true); + //var s = window.getSelection(); + //var oRange = s.getRangeAt(0); //get the text range + //var oRect = oRange.getBoundingClientRect(); + //window.scrollTo(400, 20000); + this.dimensions.y_scroll = Math.round( + window.scrollY * this.dimensions.scale_factor.height + ); + this.dimensions.x_scroll = Math.round( + window.scrollX * this.dimensions.scale_factor.width + ); + this.dimensions.update(); + this._mightSendBigFrames(); + } + _launch() { const mode = this.config.http_server_mode_type; if (mode.includes("raw_text_")) { @@ -119,9 +254,25 @@ export default MixinBase => _handleMouse(input) { switch (input.button) { case 1: - this._mouseAction("mousemove", input.mouse_x, input.mouse_y); + var y_hack = false; + if (input.hasOwnProperty("y_hack")) { + y_hack = true; + } + this._mouseAction( + "mousemove", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); if (!this._mousedown) { - this._mouseAction("mousedown", input.mouse_x, input.mouse_y); + this._mouseAction( + "mousedown", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); setTimeout(() => { this.sendSmallTextFrame(); }, 500); @@ -129,10 +280,26 @@ export default MixinBase => this._mousedown = true; break; case 0: - this._mouseAction("mousemove", input.mouse_x, input.mouse_y); + var y_hack = false; + if (input.hasOwnProperty("y_hack")) { + y_hack = true; + } + this._mouseAction( + "mousemove", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); if (this._mousedown) { - this._mouseAction("click", input.mouse_x, input.mouse_y); - this._mouseAction("mouseup", input.mouse_x, input.mouse_y); + this._mouseAction("click", input.mouse_x, input.mouse_y, 0, y_hack); + this._mouseAction( + "mouseup", + input.mouse_x, + input.mouse_y, + 0, + y_hack + ); } this._mousedown = false; break; @@ -186,8 +353,12 @@ export default MixinBase => } } - _mouseAction(type, x, y) { - const [dom_x, dom_y] = this._getDOMCoordsFromMouseCoords(x, y); + _mouseAction(type, x, y, button, y_hack = false) { + let [dom_x, dom_y] = this._getDOMCoordsFromMouseCoords(x, y); + if (y_hack) { + const [dom_x2, dom_y2] = this._getDOMCoordsFromMouseCoords(x, y + 1); + dom_y = (dom_y + dom_y2) / 2; + } const element = document.elementFromPoint( dom_x - window.scrollX, dom_y - window.scrollY @@ -208,7 +379,7 @@ export default MixinBase => false, false, false, - 0, + button, null ); element.dispatchEvent(clickEvent); From 0b7d1dc7ef4c2ffbec6f1ea5fe6ad55084905bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:18:39 -0400 Subject: [PATCH 62/77] Added vim like navigation. This is still in an early stage. --- interfacer/src/browsh/tty.go | 677 +++++++++++++++++++++++++++++++++-- 1 file changed, 650 insertions(+), 27 deletions(-) diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index 1e400290..098b447c 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -4,20 +4,110 @@ import ( "encoding/json" "fmt" "os" + "reflect" "strconv" + "strings" + "time" + "unicode" + "github.com/atotto/clipboard" "github.com/gdamore/tcell" "github.com/go-errors/errors" "github.com/spf13/viper" ) +type VimMode int + +const ( + NormalMode VimMode = iota + 1 + InsertMode + FindMode + LinkMode + LinkModeNewTab + LinkModeCopy + WaitMode + VisualMode + CaretMode + MakeMarkMode + GotoMarkMode +) + +type mark struct { + tabID int + URI string + xScroll int + yScroll int +} + +type Coordinate struct { + X, Y int +} + +type HintRect struct { + Bottom int `json:"bottom"` + Top int `json:"top"` + Left int `json:"left"` + Right int `json:"right"` + Width int `json:"width"` + Height int `json:"height"` + Href string `json:"href"` +} + var ( screen tcell.Screen uiHeight = 2 // IsMonochromeMode decides whether to render the TTY in full colour or monochrome IsMonochromeMode = false + // vimMode + vimMode = NormalMode + vimCommandsBindings = make(map[string]string) + keyEvents = make([]*tcell.EventKey, 11) + //runeTime = make(map[rune]time.Time) + //lastRune rune + waitModeStartTime time.Time + findText string + // marks + globalMarkMap = make(map[rune]*mark) + localMarkMap = make(map[int]map[rune]*mark) + // position coordinate for caret mode + caretPos Coordinate + // for link modes + linkText string + linkHintRects []HintRect + linkHintKeys = "asdfwerxcv" + linkHints []string + linkHintsToRects = make(map[string]*HintRect) + linkModeWithHints = true + linkHintWriteStringCalls *func() ) +func init() { + setupLinkHints() +} + +func setupLinkHints() { + lowerAlpha := "abcdefghijklmnopqrstuvwxyz" + missingAlpha := lowerAlpha + + // Use linkHintKeys first to generate link hints + for i := 0; i < len(linkHintKeys); i++ { + for j := 0; j < len(linkHintKeys); j++ { + linkHints = append(linkHints, string(linkHintKeys[i])+string(linkHintKeys[j])) + } + missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) + } + + // missingAlpha contains all keys that aren't in linkHintKeys + // we use this to generate the last link hint key combinations, + // so this will only be used when we run out of linkHintKeys based + // link hint key combinations. + for i := 0; i < len(missingAlpha); i++ { + for j := 0; j < len(lowerAlpha); j++ { + linkHints = append(linkHints, string(missingAlpha[i])+string(lowerAlpha[j])) + } + } +} + func setupTcell() { var err error if err = screen.Init(); err != nil { @@ -88,6 +178,7 @@ func handleUserKeyPress(ev *tcell.EventKey) { if activeInputBox != nil { handleInputBoxInput(ev) } else { + handleVimControl(ev) handleScrolling(ev) // TODO: shouldn't you be able to still use mouse scrolling? } } @@ -139,34 +230,481 @@ func isMultiLineEnter(ev *tcell.EventKey) bool { return activeInputBox.isMultiLine() && ev.Key() == 13 && ev.Modifiers() != 4 } -func handleScrolling(ev *tcell.EventKey) { +// Moves the caret in CaretMode. +// testFunc is a function that tests for the reaching of the boundaries of the given axis. +// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. +// The step size and direction is given by the value of step. +func moveVimCaret(testFunc func() bool, valRef *int, step int) { + var prevCell, nextCell, nextNextCell cell + var r rune + hasNextNextCell := false + + for testFunc() { + prevCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef += step + nextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + + if testFunc() { + *valRef += step + nextNextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef -= step + hasNextNextCell = true + } else { + hasNextNextCell = false + } + + r = nextCell.character[0] + // Check if the next cell is different in any way + if !reflect.DeepEqual(prevCell, nextCell) { + if hasNextNextCell { + // This condition should apply to the spaces between words and the like + // Checking with unicode.isSpace() didn't give correct results for some reason + // TODO: find out what that reason is and improve this + if !unicode.IsLetter(r) && unicode.IsLetter(nextNextCell.character[0]) { + continue + } + // If the upcoming cell is deeply equal we can continue to go forward + if reflect.DeepEqual(nextCell, nextNextCell) { + continue + } + } + // This cell is different and other conditions for continuing don't apply + // therefore we stop going forward. + break + } + } +} + +// TODO: This fails if the tab with mark.tabID doesn't exist anymore it should recreate said tab, then go to the mark's URL and position +func gotoMark(mark *mark) { + if CurrentTab.ID != mark.tabID { + ensureTabExists(mark.tabID) + switchToTab(mark.tabID) + } + if CurrentTab.URI != mark.URI { + sendMessageToWebExtension("/tab_command,/url," + mark.URI) + //sleep? + } + doScrollAbsolute(mark.xScroll, mark.yScroll) +} + +// Make a mark at the current position in the current tab +func makeMark() *mark { + return &mark{CurrentTab.ID, CurrentTab.URI, CurrentTab.frame.xScroll, CurrentTab.frame.yScroll} +} + +func generateLeftClickYHack(x, y int, yHack bool) { + newMouseEvent := tcell.NewEventMouse(x, y+uiHeight, tcell.Button1, 0) + handleMouseEventYHack(newMouseEvent, yHack) + time.Sleep(time.Millisecond * 100) + newMouseEvent = tcell.NewEventMouse(x, y+uiHeight, 0, 0) + handleMouseEventYHack(newMouseEvent, yHack) +} + +func generateLeftClick(x, y int) { + generateLeftClickYHack(x, y, false) +} + +// TODO: This isn't working for opening new tabs. +func generateMiddleClick(x, y int) { + newMouseEvent := tcell.NewEventMouse(x, y+uiHeight, tcell.Button2, 0) + handleMouseEvent(newMouseEvent) + time.Sleep(time.Millisecond * 100) + newMouseEvent = tcell.NewEventMouse(x, y+uiHeight, 0, 0) + handleMouseEvent(newMouseEvent) +} + +func goIntoWaitMode() { + vimMode = WaitMode + waitModeStartTime = time.Now() +} + +func updateLinkHintDisplay() { + linkHintsToRects = make(map[string]*HintRect) + lh := len(linkHintRects) + var ht string + // List of closures + var fc []*func() + + for i, r := range linkHintRects { + // When the number of link hints is small enough + // using just one key for individual link hints suffices. + // Otherwise use the prepared link hint key combinations. + if lh <= len(linkHintKeys) { + ht = string(linkHintKeys[i]) + } else { + ht = linkHints[i] + } + // Add the key combination ht to the linkHintsToRects map. + // When the user presses it, we can easily lookup the + // link hint properties associated with it. + linkHintsToRects[ht] = &linkHintRects[i] + + // When the first key got hit, + // shorten the link hints accordingly + offsetLeft := 0 + if strings.HasPrefix(ht, linkText) { + ht = ht[len(linkText):len(ht)] + offsetLeft = len(linkText) + } + + // Make copies of parameter values + rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht + + // Link hints are in upper case in new tab mode + if vimMode == LinkModeNewTab { + htCopy = strings.ToUpper(htCopy) + } + + // Create closure + f := func() { + writeString(rLeftCopy+offsetLeft, rTopCopy+uiHeight, htCopy, tcell.StyleDefault) + } + fc = append(fc, &f) + } + // Create closure that calls the other closures + ff := func() { + for _, f := range fc { + (*f)() + } + } + linkHintWriteStringCalls = &ff +} + +func eraseLinkHints() { + linkText = "" + linkHintWriteStringCalls = nil + linkHintsToRects = make(map[string]*HintRect) + linkHintRects = nil +} + +func isNormalModeKey(ev *tcell.EventKey) bool { + if ev.Key() == tcell.KeyESC { + return true + } + return false +} + +func handleVimControl(ev *tcell.EventKey) { + var lastRune rune + command := "" + + if len(keyEvents) > 0 && keyEvents[0] != nil { + lastRune = keyEvents[len(keyEvents)-1].Rune() + } + + keyEvents = append(keyEvents, ev) + if len(keyEvents) > 10 { + keyEvents = keyEvents[1:] + } + + keyCombination := string(lastRune) + string(ev.Rune()) + + switch vimMode { + case WaitMode: + if time.Since(waitModeStartTime) < time.Millisecond*1000 { + return + } + vimMode = NormalMode + fallthrough + case NormalMode: + command = vimCommandsBindings[keyCombination] + if len(command) == 0 { + keyCombination := string(ev.Rune()) + command = vimCommandsBindings[keyCombination] + } + case InsertMode: + if isNormalModeKey(ev) { + command = "normalMode" + } + case VisualMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + if ev.Rune() == 'c' { + command = "caretMode" + } + if ev.Rune() == 'o' { + //swap cursor position begin->end or end->begin + } + if ev.Rune() == 'y' { + //clipboard + } + } + case CaretMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + switch ev.Key() { + case tcell.KeyEnter: + generateLeftClick(caretPos.X, caretPos.Y-uiHeight) + } + switch ev.Rune() { + case 'v': + command = "visualMode" + case 'h': + moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) + case 'l': + width, _ := screen.Size() + moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) + case 'k': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) + if caretPos.Y < uiHeight { + command = "scrollHalfPageUp" + if CurrentTab.frame.yScroll == 0 { + caretPos.Y = uiHeight + } else { + caretPos.Y += (height - uiHeight) / 2 + } + } + case 'j': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) + if caretPos.Y > height-uiHeight { + command = "scrollHalfPageDown" + caretPos.Y -= (height - uiHeight) / 2 + } + } + } + case MakeMarkMode: + if unicode.IsLower(ev.Rune()) { + if localMarkMap[CurrentTab.ID] == nil { + localMarkMap[CurrentTab.ID] = make(map[rune]*mark) + } + localMarkMap[CurrentTab.ID][ev.Rune()] = makeMark() + } else if unicode.IsUpper(ev.Rune()) { + globalMarkMap[ev.Rune()] = makeMark() + } + + command = "normalMode" + case GotoMarkMode: + if mark, ok := globalMarkMap[ev.Rune()]; ok { + gotoMark(mark) + } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { + if mark, ok := m[ev.Rune()]; ok { + gotoMark(mark) + } + } + + command = "normalMode" + case FindMode: + if isNormalModeKey(ev) { + command = "normalMode" + findText = "" + } else { + if ev.Key() == tcell.KeyEnter { + vimMode = NormalMode + command = "findText" + break + } + if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 { + if len(findText) > 0 { + findText = findText[:len(findText)-1] + } + } else { + findText += string(ev.Rune()) + } + } + case LinkMode, LinkModeNewTab, LinkModeCopy: + if isNormalModeKey(ev) { + command = "normalMode" + eraseLinkHints() + } else { + linkText += string(ev.Rune()) + updateLinkHintDisplay() + if linkModeWithHints { + if r, ok := linkHintsToRects[linkText]; ok { + if r != nil { + switch vimMode { + case LinkMode: + if (*r).Height == 2 { + generateLeftClickYHack((*r).Left, (*r).Top, true) + } else { + generateLeftClick((*r).Left, (*r).Top) + } + case LinkModeNewTab: + //generateMiddleClick(r.Left, r.Top) + sendMessageToWebExtension("/new_tab," + r.Href) + case LinkModeCopy: + clipboard.WriteAll(r.Href) + } + goIntoWaitMode() + eraseLinkHints() + } + } + } else { + coords := findAndHighlightTextOnScreen(linkText) + if len(coords) == 1 { + goIntoWaitMode() + + if vimMode == LinkModeNewTab { + generateMiddleClick(coords[0].X, coords[0].Y) + } else { + generateLeftClick(coords[0].X, coords[0].Y) + } + linkText = "" + return + } else if len(coords) == 0 { + vimMode = NormalMode + linkText = "" + return + } + } + } + } + + if len(command) > 0 { + executeVimCommand(command) + } +} + +func executeVimCommand(command string) { + switch command { + case "urlUp": + sendMessageToWebExtension("/tab_command,/url_up") + case "urlRoot": + sendMessageToWebExtension("/tab_command,/url_root") + case "scrollToTop": + doScroll(0, -CurrentTab.frame.domRowCount()) + case "scrollToBottom": + doScroll(0, CurrentTab.frame.domRowCount()) + case "scrollUp": + doScroll(0, -1) + case "scrollDown": + doScroll(0, 1) + case "scrollLeft": + doScroll(-1, 0) + case "scrollRight": + doScroll(1, 0) + case "editURL": + urlBarFocusToggle() + case "firstTab": + switchToTab(tabsOrder[0]) + case "lastTab": + switchToTab(tabsOrder[len(tabsOrder)-1]) + case "scrollHalfPageDown": + _, height := screen.Size() + doScroll(0, (height-uiHeight)/2) + case "scrollHalfPageUp": + _, height := screen.Size() + doScroll(0, -((height - uiHeight) / 2)) + case "historyBack": + sendMessageToWebExtension("/tab_command,/history_back") + case "historyForward": + sendMessageToWebExtension("/tab_command,/history_forward") + case "reload": + sendMessageToWebExtension("/tab_command,/reload") + case "prevTab": + prevTab() + case "nextTab": + nextTab() + case "previouslyVisitedTab": + previouslyVisitedTab() + case "newTab": + createNewEmptyTab() + case "removeTab": + removeTab(CurrentTab.ID) + case "restoreTab": + restoreTab() + case "duplicateTab": + duplicateTab(CurrentTab.ID) + case "moveTabLeft": + moveTabLeft(CurrentTab.ID) + case "moveTabRight": + moveTabRight(CurrentTab.ID) + case "copyURL": + clipboard.WriteAll(CurrentTab.URI) + case "openClipboardURL": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/tab_command,/url," + URI) + case "openClipboardURLInNewTab": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/new_tab," + URI) + case "focusFirstTextInput": + sendMessageToWebExtension("/tab_command,/focus_first_text_input") + case "followLinkLabeledNext": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_next") + case "followLinkLabeledPrevious": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_previous") + case "viewHelp": + sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") + case "openLinkInCurrentTab": + vimMode = LinkMode + sendMessageToWebExtension("/tab_command,/get_clickable_hints") + eraseLinkHints() + case "openLinkInNewTab": + vimMode = LinkModeNewTab + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "copyLinkURL": + vimMode = LinkModeCopy + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "findText": + fallthrough + case "findNext": + sendMessageToWebExtension("/tab_command,/find_next," + findText) + case "findPrevious": + sendMessageToWebExtension("/tab_command,/find_previous," + findText) + case "makeMark": + vimMode = MakeMarkMode + case "gotoMark": + vimMode = GotoMarkMode + case "insertMode": + vimMode = InsertMode + case "findMode": + vimMode = FindMode + case "normalMode": + vimMode = NormalMode + case "visualMode": + vimMode = VisualMode + case "caretMode": + vimMode = CaretMode + width, height := screen.Size() + caretPos.X, caretPos.Y = width/2, height/2 + } +} + +func doScroll(relX int, relY int) { + doScrollAbsolute(CurrentTab.frame.xScroll+relX, CurrentTab.frame.yScroll+relY) +} + +func doScrollAbsolute(absX int, absY int) { yScrollOriginal := CurrentTab.frame.yScroll _, height := screen.Size() height -= uiHeight + + CurrentTab.frame.yScroll = absY + CurrentTab.frame.xScroll = absX + + CurrentTab.frame.limitScroll(height) + sendMessageToWebExtension( + fmt.Sprintf("/tab_command,/scroll_status,%d,%d", + CurrentTab.frame.xScroll, CurrentTab.frame.yScroll*2)) + if CurrentTab.frame.yScroll != yScrollOriginal { + renderCurrentTabWindow() + } +} + +func handleScrolling(ev *tcell.EventKey) { + _, height := screen.Size() + height -= uiHeight if ev.Key() == tcell.KeyUp { - CurrentTab.frame.yScroll -= 2 + doScroll(0, -2) } if ev.Key() == tcell.KeyDown { - CurrentTab.frame.yScroll += 2 + doScroll(0, 2) } if ev.Key() == tcell.KeyPgUp { - CurrentTab.frame.yScroll -= height + doScroll(0, -height) } if ev.Key() == tcell.KeyPgDn { - CurrentTab.frame.yScroll += height - } - CurrentTab.frame.limitScroll(height) - sendMessageToWebExtension( - fmt.Sprintf( - "/tab_command,/scroll_status,%d,%d", - CurrentTab.frame.xScroll, - CurrentTab.frame.yScroll*2)) - if CurrentTab.frame.yScroll != yScrollOriginal { - renderCurrentTabWindow() + doScroll(0, height) } } -func handleMouseEvent(ev *tcell.EventMouse) { +func handleMouseEventYHack(ev *tcell.EventMouse, yHack bool) { if CurrentTab == nil { return } @@ -186,27 +724,24 @@ func handleMouseEvent(ev *tcell.EventMouse) { "mouse_y": int(yInFrame), "modifiers": int(ev.Modifiers()), } + if yHack { + eventMap["y_hack"] = true + } marshalled, _ := json.Marshal(eventMap) sendMessageToWebExtension("/stdin," + string(marshalled)) } +func handleMouseEvent(ev *tcell.EventMouse) { + handleMouseEventYHack(ev, false) +} + func handleMouseScroll(scrollType tcell.ButtonMask) { - yScrollOriginal := CurrentTab.frame.yScroll _, height := screen.Size() height -= uiHeight if scrollType == tcell.WheelUp { - CurrentTab.frame.yScroll -= 1 + doScroll(0, -1) } else if scrollType == tcell.WheelDown { - CurrentTab.frame.yScroll += 1 - } - CurrentTab.frame.limitScroll(height) - sendMessageToWebExtension( - fmt.Sprintf( - "/tab_command,/scroll_status,%d,%d", - CurrentTab.frame.xScroll, - CurrentTab.frame.yScroll*2)) - if CurrentTab.frame.yScroll != yScrollOriginal { - renderCurrentTabWindow() + doScroll(0, 1) } } @@ -255,10 +790,98 @@ func renderCurrentTabWindow() { activeInputBox.renderCursor() } overlayPageStatusMessage() + overlayVimMode() overlayCallToSupport() screen.Show() } +func searchScreenForText(text string) []Coordinate { + var offsets = make([]Coordinate, 0) + var splitString []string + var r rune + var s string + width, height := screen.Size() + screenText := "" + index := 0 + + for y := 0; y < height-uiHeight; y++ { + screenText = "" + for x := 0; x < width; x++ { + r = getCell(x, y).character[0] + s = string(r) + if len(s) == 0 || len(s) > 1 { + screenText += " " + } else { + screenText += string(getCell(x, y).character[0]) + } + } + index = 0 + splitString = strings.Split(strings.ToLower(screenText), strings.ToLower(text)) + for _, s := range splitString { + if index+len(s) >= width { + break + } + + offsets = append(offsets, Coordinate{index + len(s), y}) + index += len(s) + len(text) + } + } + return offsets +} + +func findAndHighlightTextOnScreen(text string) []Coordinate { + var x, y int + var styling = tcell.StyleDefault + + offsets := searchScreenForText(text) + for _, offset := range offsets { + y = offset.Y + x = offset.X + for z := 0; z < len(text); z++ { + screen.SetContent(x+z, y+uiHeight, rune(text[z]), nil, styling) + } + } + screen.Show() + return offsets +} + +// Parse incoming link hints +func parseJSONLinkHints(jsonString string) { + jsonBytes := []byte(jsonString) + if err := json.Unmarshal(jsonBytes, &linkHintRects); err != nil { + Shutdown(err) + } + + // Optimize link hint positions + for i := 0; i < len(linkHintRects); i++ { + r := &linkHintRects[i] + + // For links that are more than one line high + // we want to position the link hint in the vertical middle + if r.Height > 2 { + if r.Height%2 == 0 { + r.Top += r.Height / 2 + } else { + r.Top += r.Height/2 - 1 + } + } + + // For links that are more one character long we try to move + // the link hint two characters to the right, if possible. + if r.Width > 1 { + o := r.Left + r.Left += r.Width/2 - 1 + if r.Left > o+2 { + r.Left = o + 2 + } + } + } + + Log("Received parseJSONLinkHint") + // This is where the display of actual link hints is prepared + updateLinkHintDisplay() +} + func getCell(x, y int) cell { var currentCell cell var ok bool From bd5c30640db90c7f926d5703dee1844d5869ea9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:20:44 -0400 Subject: [PATCH 63/77] Instead of adding 1 to Y coord, add 1 to height. This fixes an issues with activating input boxes using link hinting. --- interfacer/src/browsh/frame_builder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index afb53c38..37f90eed 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -160,9 +160,9 @@ func (f *frame) updateInputBoxes(incoming incomingFrameText) { inputBox := f.inputBoxes[incomingInputBox.ID] inputBox.X = incomingInputBox.X // TODO: Why do we have to add the 1 to the y coord?? - inputBox.Y = (incomingInputBox.Y + 1) / 2 + inputBox.Y = (incomingInputBox.Y + 0) / 2 inputBox.Width = incomingInputBox.Width - inputBox.Height = incomingInputBox.Height / 2 + inputBox.Height = (incomingInputBox.Height / 2) + 1 inputBox.FgColour = incomingInputBox.FgColour inputBox.TagName = incomingInputBox.TagName inputBox.Type = incomingInputBox.Type From d3fff67c61b67ac2f0924818da8c1fdc3c45fabc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 10:35:22 -0400 Subject: [PATCH 64/77] Prettified js files --- webext/src/background/tty_commands_mixin.js | 10 +- webext/src/vimium.js | 1686 ++++++++++++------- 2 files changed, 1060 insertions(+), 636 deletions(-) diff --git a/webext/src/background/tty_commands_mixin.js b/webext/src/background/tty_commands_mixin.js index 815674b6..dc33b59a 100644 --- a/webext/src/background/tty_commands_mixin.js +++ b/webext/src/background/tty_commands_mixin.js @@ -186,16 +186,16 @@ export default MixinBase => } restoreTab() { - var sessionsInfo = browser.sessions.getRecentlyClosed({maxResults: 1 }); + var sessionsInfo = browser.sessions.getRecentlyClosed({ maxResults: 1 }); sessionsInfo.then(this._restoreTab); } - + _restoreTab(sessionsInfo) { var mySessionInfo = sessionsInfo[0]; if (mySessionInfo.tab) { - browser.sessions.restore(mySessionInfo.tab.sessionId); - } else { - browser.sessions.restore(mySessionInfo.window.sessionId); + browser.sessions.restore(mySessionInfo.tab.sessionId); + } else { + browser.sessions.restore(mySessionInfo.window.sessionId); } } diff --git a/webext/src/vimium.js b/webext/src/vimium.js index 6463294f..246c5353 100644 --- a/webext/src/vimium.js +++ b/webext/src/vimium.js @@ -6,7 +6,7 @@ // to better fullfill the needs of Browsh: // // In line 11 of getLocalHints the following code was added: -// +// // if (requireHref && element.href && visibleElement.length > 0) { // visibleElement[0]["href"] = element.href; // } @@ -27,7 +27,7 @@ // The following lines just before the return statement in getLocalHints were // commented out, because we're currently not using this functionality and the // settings for it simply don't exist in Browsh (yet). -// +// // /*if (Settings.get("filterLinkHints")) { // for (m = 0, len3 = localHints.length; m < len3; m++) { // hint = localHints[m]; @@ -39,81 +39,114 @@ // because Browsh is currently assuming that it is being run in Firefox. var Rect = { - create: function(x1, y1, x2, y2) { - return { - bottom: y2, - top: y1, - left: x1, - right: x2, - width: x2 - x1, - height: y2 - y1 - }; - }, - copy: function(rect) { - return { - bottom: rect.bottom, - top: rect.top, - left: rect.left, - right: rect.right, - width: rect.width, - height: rect.height - }; - }, - translate: function(rect, x, y) { - if (x == null) { - x = 0; - } - if (y == null) { - y = 0; - } - return { - bottom: rect.bottom + y, - top: rect.top + y, - left: rect.left + x, - right: rect.right + x, - width: rect.width, - height: rect.height + create: function(x1, y1, x2, y2) { + return { + bottom: y2, + top: y1, + left: x1, + right: x2, + width: x2 - x1, + height: y2 - y1 }; - }, - subtract: function(rect1, rect2) { - var rects; - rect2 = this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); - if (rect2.width < 0 || rect2.height < 0) { - return [Rect.copy(rect1)]; - } - rects = [this.create(rect1.left, rect1.top, rect2.left, rect2.top), this.create(rect2.left, rect1.top, rect2.right, rect2.top), this.create(rect2.right, rect1.top, rect1.right, rect2.top), this.create(rect1.left, rect2.top, rect2.left, rect2.bottom), this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom)]; - return rects.filter(function(rect) { - return rect.height > 0 && rect.width > 0; - }); - }, - intersects: function(rect1, rect2) { - return rect1.right > rect2.left && rect1.left < rect2.right && rect1.bottom > rect2.top && rect1.top < rect2.bottom; - }, - intersectsStrict: function(rect1, rect2) { - return rect1.right >= rect2.left && rect1.left <= rect2.right && rect1.bottom >= rect2.top && rect1.top <= rect2.bottom; - }, - equals: function(rect1, rect2) { - var i, len, property, ref; - ref = ["top", "bottom", "left", "right", "width", "height"]; - for (i = 0, len = ref.length; i < len; i++) { - property = ref[i]; - if (rect1[property] !== rect2[property]) { - return false; - } + }, + copy: function(rect) { + return { + bottom: rect.bottom, + top: rect.top, + left: rect.left, + right: rect.right, + width: rect.width, + height: rect.height + }; + }, + translate: function(rect, x, y) { + if (x == null) { + x = 0; + } + if (y == null) { + y = 0; + } + return { + bottom: rect.bottom + y, + top: rect.top + y, + left: rect.left + x, + right: rect.right + x, + width: rect.width, + height: rect.height + }; + }, + subtract: function(rect1, rect2) { + var rects; + rect2 = this.create( + Math.max(rect1.left, rect2.left), + Math.max(rect1.top, rect2.top), + Math.min(rect1.right, rect2.right), + Math.min(rect1.bottom, rect2.bottom) + ); + if (rect2.width < 0 || rect2.height < 0) { + return [Rect.copy(rect1)]; + } + rects = [ + this.create(rect1.left, rect1.top, rect2.left, rect2.top), + this.create(rect2.left, rect1.top, rect2.right, rect2.top), + this.create(rect2.right, rect1.top, rect1.right, rect2.top), + this.create(rect1.left, rect2.top, rect2.left, rect2.bottom), + this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), + this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), + this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), + this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom) + ]; + return rects.filter(function(rect) { + return rect.height > 0 && rect.width > 0; + }); + }, + intersects: function(rect1, rect2) { + return ( + rect1.right > rect2.left && + rect1.left < rect2.right && + rect1.bottom > rect2.top && + rect1.top < rect2.bottom + ); + }, + intersectsStrict: function(rect1, rect2) { + return ( + rect1.right >= rect2.left && + rect1.left <= rect2.right && + rect1.bottom >= rect2.top && + rect1.top <= rect2.bottom + ); + }, + equals: function(rect1, rect2) { + var i, len, property, ref; + ref = ["top", "bottom", "left", "right", "width", "height"]; + for (i = 0, len = ref.length; i < len; i++) { + property = ref[i]; + if (rect1[property] !== rect2[property]) { + return false; } - return true; - }, - intersect: function(rect1, rect2) { - return this.create(Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom)); } - }; + return true; + }, + intersect: function(rect1, rect2) { + return this.create( + Math.max(rect1.left, rect2.left), + Math.max(rect1.top, rect2.top), + Math.min(rect1.right, rect2.right), + Math.min(rect1.bottom, rect2.bottom) + ); + } +}; var DomUtils = { - documentReady: function() { - var callbacks, isReady, onDOMContentLoaded, ref; - ref = [document.readyState !== "loading", []], isReady = ref[0], callbacks = ref[1]; - if (!isReady) { - window.addEventListener("DOMContentLoaded", onDOMContentLoaded = forTrusted(function() { + documentReady: function() { + var callbacks, isReady, onDOMContentLoaded, ref; + (ref = [document.readyState !== "loading", []]), + (isReady = ref[0]), + (callbacks = ref[1]); + if (!isReady) { + window.addEventListener( + "DOMContentLoaded", + (onDOMContentLoaded = forTrusted(function() { var callback, i, len; window.removeEventListener("DOMContentLoaded", onDOMContentLoaded); isReady = true; @@ -121,408 +154,696 @@ var DomUtils = { callback = callbacks[i]; callback(); } - return callbacks = null; - })); + return (callbacks = null); + })) + ); + } + return function(callback) { + if (isReady) { + return callback(); + } else { + return callbacks.push(callback); } - return function(callback) { - if (isReady) { - return callback(); - } else { - return callbacks.push(callback); - } - }; - }, - getVisibleClientRect: function(element, testChildren) { - var child, childClientRect, clientRect, clientRects, computedStyle, i, isInlineZeroHeight, j, len, len1, ref, ref1; - if (testChildren == null) { - testChildren = false; + }; + }, + getVisibleClientRect: function(element, testChildren) { + var child, + childClientRect, + clientRect, + clientRects, + computedStyle, + i, + isInlineZeroHeight, + j, + len, + len1, + ref, + ref1; + if (testChildren == null) { + testChildren = false; + } + clientRects = (function() { + var i, len, ref, results; + ref = element.getClientRects(); + results = []; + for (i = 0, len = ref.length; i < len; i++) { + clientRect = ref[i]; + results.push(Rect.copy(clientRect)); } - clientRects = (function() { - var i, len, ref, results; - ref = element.getClientRects(); - results = []; - for (i = 0, len = ref.length; i < len; i++) { - clientRect = ref[i]; - results.push(Rect.copy(clientRect)); - } - return results; - })(); + return results; + })(); + isInlineZeroHeight = function() { + var elementComputedStyle, isInlineZeroFontSize; + elementComputedStyle = window.getComputedStyle(element, null); + isInlineZeroFontSize = + 0 === + elementComputedStyle.getPropertyValue("display").indexOf("inline") && + elementComputedStyle.getPropertyValue("font-size") === "0px"; isInlineZeroHeight = function() { - var elementComputedStyle, isInlineZeroFontSize; - elementComputedStyle = window.getComputedStyle(element, null); - isInlineZeroFontSize = (0 === elementComputedStyle.getPropertyValue("display").indexOf("inline")) && (elementComputedStyle.getPropertyValue("font-size") === "0px"); - isInlineZeroHeight = function() { - return isInlineZeroFontSize; - }; return isInlineZeroFontSize; }; - for (i = 0, len = clientRects.length; i < len; i++) { - clientRect = clientRects[i]; - if ((clientRect.width === 0 || clientRect.height === 0) && testChildren) { - ref = element.children; - for (j = 0, len1 = ref.length; j < len1; j++) { - child = ref[j]; - computedStyle = window.getComputedStyle(child, null); - if (computedStyle.getPropertyValue("float") === "none" && !((ref1 = computedStyle.getPropertyValue("position")) === "absolute" || ref1 === "fixed") && !(clientRect.height === 0 && isInlineZeroHeight() && 0 === computedStyle.getPropertyValue("display").indexOf("inline"))) { - continue; - } - childClientRect = this.getVisibleClientRect(child, true); - if (childClientRect === null || childClientRect.width < 3 || childClientRect.height < 3) { - continue; - } - return childClientRect; - } - } else { - clientRect = this.cropRectToVisible(clientRect); - if (clientRect === null || clientRect.width < 3 || clientRect.height < 3) { + return isInlineZeroFontSize; + }; + for (i = 0, len = clientRects.length; i < len; i++) { + clientRect = clientRects[i]; + if ((clientRect.width === 0 || clientRect.height === 0) && testChildren) { + ref = element.children; + for (j = 0, len1 = ref.length; j < len1; j++) { + child = ref[j]; + computedStyle = window.getComputedStyle(child, null); + if ( + computedStyle.getPropertyValue("float") === "none" && + !( + (ref1 = computedStyle.getPropertyValue("position")) === + "absolute" || ref1 === "fixed" + ) && + !( + clientRect.height === 0 && + isInlineZeroHeight() && + 0 === computedStyle.getPropertyValue("display").indexOf("inline") + ) + ) { continue; } - computedStyle = window.getComputedStyle(element, null); - if (computedStyle.getPropertyValue('visibility') !== 'visible') { + childClientRect = this.getVisibleClientRect(child, true); + if ( + childClientRect === null || + childClientRect.width < 3 || + childClientRect.height < 3 + ) { continue; } - return clientRect; + return childClientRect; } + } else { + clientRect = this.cropRectToVisible(clientRect); + if ( + clientRect === null || + clientRect.width < 3 || + clientRect.height < 3 + ) { + continue; + } + computedStyle = window.getComputedStyle(element, null); + if (computedStyle.getPropertyValue("visibility") !== "visible") { + continue; + } + return clientRect; } + } + return null; + }, + cropRectToVisible: function(rect) { + var boundedRect; + boundedRect = Rect.create( + Math.max(rect.left, 0), + Math.max(rect.top, 0), + rect.right, + rect.bottom + ); + if ( + boundedRect.top >= window.innerHeight - 4 || + boundedRect.left >= window.innerWidth - 4 + ) { return null; - }, - cropRectToVisible: function(rect) { - var boundedRect; - boundedRect = Rect.create(Math.max(rect.left, 0), Math.max(rect.top, 0), rect.right, rect.bottom); - if (boundedRect.top >= window.innerHeight - 4 || boundedRect.left >= window.innerWidth - 4) { - return null; + } else { + return boundedRect; + } + }, + getClientRectsForAreas: function(imgClientRect, areas) { + var area, + coords, + diff, + i, + len, + r, + rect, + rects, + ref, + shape, + x, + x1, + x2, + y, + y1, + y2; + rects = []; + for (i = 0, len = areas.length; i < len; i++) { + area = areas[i]; + coords = area.coords.split(",").map(function(coord) { + return parseInt(coord, 10); + }); + shape = area.shape.toLowerCase(); + if (shape === "rect" || shape === "rectangle") { + (x1 = coords[0]), (y1 = coords[1]), (x2 = coords[2]), (y2 = coords[3]); + } else if (shape === "circle" || shape === "circ") { + (x = coords[0]), (y = coords[1]), (r = coords[2]); + diff = r / Math.sqrt(2); + x1 = x - diff; + x2 = x + diff; + y1 = y - diff; + y2 = y + diff; + } else if (shape === "default") { + (ref = [0, 0, imgClientRect.width, imgClientRect.height]), + (x1 = ref[0]), + (y1 = ref[1]), + (x2 = ref[2]), + (y2 = ref[3]); } else { - return boundedRect; + (x1 = coords[0]), (y1 = coords[1]), (x2 = coords[2]), (y2 = coords[3]); } - }, - getClientRectsForAreas: function(imgClientRect, areas) { - var area, coords, diff, i, len, r, rect, rects, ref, shape, x, x1, x2, y, y1, y2; - rects = []; - for (i = 0, len = areas.length; i < len; i++) { - area = areas[i]; - coords = area.coords.split(",").map(function(coord) { - return parseInt(coord, 10); + rect = Rect.translate( + Rect.create(x1, y1, x2, y2), + imgClientRect.left, + imgClientRect.top + ); + rect = this.cropRectToVisible(rect); + if (rect && !isNaN(rect.top)) { + rects.push({ + element: area, + rect: rect }); - shape = area.shape.toLowerCase(); - if (shape === "rect" || shape === "rectangle") { - x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; - } else if (shape === "circle" || shape === "circ") { - x = coords[0], y = coords[1], r = coords[2]; - diff = r / Math.sqrt(2); - x1 = x - diff; - x2 = x + diff; - y1 = y - diff; - y2 = y + diff; - } else if (shape === "default") { - ref = [0, 0, imgClientRect.width, imgClientRect.height], x1 = ref[0], y1 = ref[1], x2 = ref[2], y2 = ref[3]; - } else { - x1 = coords[0], y1 = coords[1], x2 = coords[2], y2 = coords[3]; - } - rect = Rect.translate(Rect.create(x1, y1, x2, y2), imgClientRect.left, imgClientRect.top); - rect = this.cropRectToVisible(rect); - if (rect && !isNaN(rect.top)) { - rects.push({ - element: area, - rect: rect - }); - } } - return rects; - }, - isSelectable: function(element) { - var unselectableTypes; - if (!(element instanceof Element)) { - return false; + } + return rects; + }, + isSelectable: function(element) { + var unselectableTypes; + if (!(element instanceof Element)) { + return false; + } + unselectableTypes = [ + "button", + "checkbox", + "color", + "file", + "hidden", + "image", + "radio", + "reset", + "submit" + ]; + return ( + (element.nodeName.toLowerCase() === "input" && + unselectableTypes.indexOf(element.type) === -1) || + element.nodeName.toLowerCase() === "textarea" || + element.isContentEditable + ); + }, + getViewportTopLeft: function() { + var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; + box = document.documentElement; + style = getComputedStyle(box); + rect = box.getBoundingClientRect(); + if ( + style.position === "static" && + !/content|paint|strict/.test(style.contain || "") + ) { + marginTop = parseInt(style.marginTop); + marginLeft = parseInt(style.marginLeft); + return { + top: -rect.top + marginTop, + left: -rect.left + marginLeft + }; + } else { + //if (Utils.isFirefox()) + if (true) { + clientTop = parseInt(style.borderTopWidth); + clientLeft = parseInt(style.borderLeftWidth); + } else { + (clientTop = box.clientTop), (clientLeft = box.clientLeft); } - unselectableTypes = ["button", "checkbox", "color", "file", "hidden", "image", "radio", "reset", "submit"]; - return (element.nodeName.toLowerCase() === "input" && unselectableTypes.indexOf(element.type) === -1) || element.nodeName.toLowerCase() === "textarea" || element.isContentEditable; - }, - getViewportTopLeft: function() { - var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; - box = document.documentElement; - style = getComputedStyle(box); - rect = box.getBoundingClientRect(); - if (style.position === "static" && !/content|paint|strict/.test(style.contain || "")) { - marginTop = parseInt(style.marginTop); - marginLeft = parseInt(style.marginLeft); - return { - top: -rect.top + marginTop, - left: -rect.left + marginLeft - }; + return { + top: -rect.top - clientTop, + left: -rect.left - clientLeft + }; + } + }, + makeXPath: function(elementArray) { + var element, i, len, xpath; + xpath = []; + for (i = 0, len = elementArray.length; i < len; i++) { + element = elementArray[i]; + xpath.push(".//" + element, ".//xhtml:" + element); + } + return xpath.join(" | "); + }, + evaluateXPath: function(xpath, resultType) { + var contextNode, namespaceResolver; + contextNode = document.webkitIsFullScreen + ? document.webkitFullscreenElement + : document.documentElement; + namespaceResolver = function(namespace) { + if (namespace === "xhtml") { + return "http://www.w3.org/1999/xhtml"; } else { - //if (Utils.isFirefox()) - if (true) { - clientTop = parseInt(style.borderTopWidth); - clientLeft = parseInt(style.borderLeftWidth); - } else { - clientTop = box.clientTop, clientLeft = box.clientLeft; - } - return { - top: -rect.top - clientTop, - left: -rect.left - clientLeft - }; + return null; } - }, - makeXPath: function(elementArray) { - var element, i, len, xpath; - xpath = []; - for (i = 0, len = elementArray.length; i < len; i++) { - element = elementArray[i]; - xpath.push(".//" + element, ".//xhtml:" + element); - } - return xpath.join(" | "); - }, - evaluateXPath: function(xpath, resultType) { - var contextNode, namespaceResolver; - contextNode = document.webkitIsFullScreen ? document.webkitFullscreenElement : document.documentElement; - namespaceResolver = function(namespace) { - if (namespace === "xhtml") { - return "http://www.w3.org/1999/xhtml"; - } else { - return null; - } - }; - return document.evaluate(xpath, contextNode, namespaceResolver, resultType, null); - }, - simulateClick: function(element, modifiers) { - var defaultActionShouldTrigger, event, eventSequence, i, len, results; - if (modifiers == null) { - modifiers = {}; - } - eventSequence = ["mouseover", "mousedown", "mouseup", "click"]; - results = []; - for (i = 0, len = eventSequence.length; i < len; i++) { - event = eventSequence[i]; - defaultActionShouldTrigger = /*Utils.isFirefox() &&*/ Object.keys(modifiers).length === 0 && event === "click" && element.target === "_blank" && element.href && !element.hasAttribute("onclick") && !element.hasAttribute("_vimium-has-onclick-listener") ? true : this.simulateMouseEvent(event, element, modifiers); - if (event === "click" && defaultActionShouldTrigger /*&& Utils.isFirefox()*/) { - if (0 < Object.keys(modifiers).length || element.target === "_blank") { - DomUtils.simulateClickDefaultAction(element, modifiers); - } - } - results.push(defaultActionShouldTrigger); - } - return results; - }, - simulateMouseEvent: (function() { - var lastHoveredElement; - lastHoveredElement = void 0; - return function(event, element, modifiers) { - var mouseEvent; - if (modifiers == null) { - modifiers = {}; - } - if (event === "mouseout") { - if (element == null) { - element = lastHoveredElement; - } - lastHoveredElement = void 0; - if (element == null) { - return; - } - } else if (event === "mouseover") { - this.simulateMouseEvent("mouseout", void 0, modifiers); - lastHoveredElement = element; - } - mouseEvent = document.createEvent("MouseEvents"); - mouseEvent.initMouseEvent(event, true, true, window, 1, 0, 0, 0, 0, modifiers.ctrlKey, modifiers.altKey, modifiers.shiftKey, modifiers.metaKey, 0, null); - return element.dispatchEvent(mouseEvent); - }; - })(), - simulateClickDefaultAction: function(element, modifiers) { - var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; - if (modifiers == null) { - modifiers = {}; - } - if (!(((ref = element.tagName) != null ? ref.toLowerCase() : void 0) === "a" && (element.href != null))) { - return; - } - ctrlKey = modifiers.ctrlKey, shiftKey = modifiers.shiftKey, metaKey = modifiers.metaKey, altKey = modifiers.altKey; - if (KeyboardUtils.platform === "Mac") { - newTabModifier = metaKey === true && ctrlKey === false; - } else { - newTabModifier = metaKey === false && ctrlKey === true; - } - if (newTabModifier) { - chrome.runtime.sendMessage({ - handler: "openUrlInNewTab", - url: element.href, - active: shiftKey === true - }); - } else if (shiftKey === true && metaKey === false && ctrlKey === false && altKey === false) { - chrome.runtime.sendMessage({ - handler: "openUrlInNewWindow", - url: element.href - }); - } else if (element.target === "_blank") { - chrome.runtime.sendMessage({ - handler: "openUrlInNewTab", - url: element.href, - active: true - }); - } - } -} - - var LocalHints = { - getVisibleClickable: function(element) { - var actionName, areas, areasAndRects, base1, clientRect, contentEditable, eventType, i, imgClientRects, isClickable, jsactionRule, jsactionRules, len, map, mapName, namespace, onlyHasTabIndex, possibleFalsePositive, reason, ref, ref1, ref10, ref11, ref12, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, role, ruleSplit, tabIndex, tabIndexValue, tagName, visibleElements, slice; - tagName = (ref = typeof (base1 = element.tagName).toLowerCase === "function" ? base1.toLowerCase() : void 0) != null ? ref : ""; - isClickable = false; - onlyHasTabIndex = false; - possibleFalsePositive = false; - visibleElements = []; - reason = null; - slice = [].slice; - if (tagName === "img") { - mapName = element.getAttribute("usemap"); - if (mapName) { - imgClientRects = element.getClientRects(); - mapName = mapName.replace(/^#/, "").replace("\"", "\\\""); - map = document.querySelector("map[name=\"" + mapName + "\"]"); - if (map && imgClientRects.length > 0) { - areas = map.getElementsByTagName("area"); - areasAndRects = DomUtils.getClientRectsForAreas(imgClientRects[0], areas); - visibleElements.push.apply(visibleElements, areasAndRects); - } + }; + return document.evaluate( + xpath, + contextNode, + namespaceResolver, + resultType, + null + ); + }, + simulateClick: function(element, modifiers) { + var defaultActionShouldTrigger, event, eventSequence, i, len, results; + if (modifiers == null) { + modifiers = {}; + } + eventSequence = ["mouseover", "mousedown", "mouseup", "click"]; + results = []; + for (i = 0, len = eventSequence.length; i < len; i++) { + event = eventSequence[i]; + defaultActionShouldTrigger = + /*Utils.isFirefox() &&*/ Object.keys(modifiers).length === 0 && + event === "click" && + element.target === "_blank" && + element.href && + !element.hasAttribute("onclick") && + !element.hasAttribute("_vimium-has-onclick-listener") + ? true + : this.simulateMouseEvent(event, element, modifiers); + if ( + event === "click" && + defaultActionShouldTrigger /*&& Utils.isFirefox()*/ + ) { + if (0 < Object.keys(modifiers).length || element.target === "_blank") { + DomUtils.simulateClickDefaultAction(element, modifiers); } } - if (((ref1 = (ref2 = element.getAttribute("aria-hidden")) != null ? ref2.toLowerCase() : void 0) === "" || ref1 === "true") || ((ref3 = (ref4 = element.getAttribute("aria-disabled")) != null ? ref4.toLowerCase() : void 0) === "" || ref3 === "true")) { - return []; + results.push(defaultActionShouldTrigger); + } + return results; + }, + simulateMouseEvent: (function() { + var lastHoveredElement; + lastHoveredElement = void 0; + return function(event, element, modifiers) { + var mouseEvent; + if (modifiers == null) { + modifiers = {}; } - if (this.checkForAngularJs == null) { - this.checkForAngularJs = (function() { - var angularElements, i, k, len, len1, ngAttributes, prefix, ref5, ref6, separator; - angularElements = document.getElementsByClassName("ng-scope"); - if (angularElements.length === 0) { - return function() { - return false; - }; - } else { - ngAttributes = []; - ref5 = ['', 'data-', 'x-']; - for (i = 0, len = ref5.length; i < len; i++) { - prefix = ref5[i]; - ref6 = ['-', ':', '_']; - for (k = 0, len1 = ref6.length; k < len1; k++) { - separator = ref6[k]; - ngAttributes.push(prefix + "ng" + separator + "click"); - } - } - return function(element) { - var attribute, l, len2; - for (l = 0, len2 = ngAttributes.length; l < len2; l++) { - attribute = ngAttributes[l]; - if (element.hasAttribute(attribute)) { - return true; - } - } - return false; - }; - } - })(); + if (event === "mouseout") { + if (element == null) { + element = lastHoveredElement; + } + lastHoveredElement = void 0; + if (element == null) { + return; + } + } else if (event === "mouseover") { + this.simulateMouseEvent("mouseout", void 0, modifiers); + lastHoveredElement = element; } - isClickable || (isClickable = this.checkForAngularJs(element)); - if (element.hasAttribute("onclick") || (role = element.getAttribute("role")) && ((ref5 = role.toLowerCase()) === "button" || ref5 === "tab" || ref5 === "link" || ref5 === "checkbox" || ref5 === "menuitem" || ref5 === "menuitemcheckbox" || ref5 === "menuitemradio") || (contentEditable = element.getAttribute("contentEditable")) && ((ref6 = contentEditable.toLowerCase()) === "" || ref6 === "contenteditable" || ref6 === "true")) { - isClickable = true; + mouseEvent = document.createEvent("MouseEvents"); + mouseEvent.initMouseEvent( + event, + true, + true, + window, + 1, + 0, + 0, + 0, + 0, + modifiers.ctrlKey, + modifiers.altKey, + modifiers.shiftKey, + modifiers.metaKey, + 0, + null + ); + return element.dispatchEvent(mouseEvent); + }; + })(), + simulateClickDefaultAction: function(element, modifiers) { + var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; + if (modifiers == null) { + modifiers = {}; + } + if ( + !( + ((ref = element.tagName) != null ? ref.toLowerCase() : void 0) === + "a" && element.href != null + ) + ) { + return; + } + (ctrlKey = modifiers.ctrlKey), + (shiftKey = modifiers.shiftKey), + (metaKey = modifiers.metaKey), + (altKey = modifiers.altKey); + if (KeyboardUtils.platform === "Mac") { + newTabModifier = metaKey === true && ctrlKey === false; + } else { + newTabModifier = metaKey === false && ctrlKey === true; + } + if (newTabModifier) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: shiftKey === true + }); + } else if ( + shiftKey === true && + metaKey === false && + ctrlKey === false && + altKey === false + ) { + chrome.runtime.sendMessage({ + handler: "openUrlInNewWindow", + url: element.href + }); + } else if (element.target === "_blank") { + chrome.runtime.sendMessage({ + handler: "openUrlInNewTab", + url: element.href, + active: true + }); + } + } +}; + +var LocalHints = { + getVisibleClickable: function(element) { + var actionName, + areas, + areasAndRects, + base1, + clientRect, + contentEditable, + eventType, + i, + imgClientRects, + isClickable, + jsactionRule, + jsactionRules, + len, + map, + mapName, + namespace, + onlyHasTabIndex, + possibleFalsePositive, + reason, + ref, + ref1, + ref10, + ref11, + ref12, + ref2, + ref3, + ref4, + ref5, + ref6, + ref7, + ref8, + ref9, + role, + ruleSplit, + tabIndex, + tabIndexValue, + tagName, + visibleElements, + slice; + tagName = + (ref = + typeof (base1 = element.tagName).toLowerCase === "function" + ? base1.toLowerCase() + : void 0) != null + ? ref + : ""; + isClickable = false; + onlyHasTabIndex = false; + possibleFalsePositive = false; + visibleElements = []; + reason = null; + slice = [].slice; + if (tagName === "img") { + mapName = element.getAttribute("usemap"); + if (mapName) { + imgClientRects = element.getClientRects(); + mapName = mapName.replace(/^#/, "").replace('"', '\\"'); + map = document.querySelector('map[name="' + mapName + '"]'); + if (map && imgClientRects.length > 0) { + areas = map.getElementsByTagName("area"); + areasAndRects = DomUtils.getClientRectsForAreas( + imgClientRects[0], + areas + ); + visibleElements.push.apply(visibleElements, areasAndRects); + } } - if (!isClickable && element.hasAttribute("jsaction")) { - jsactionRules = element.getAttribute("jsaction").split(";"); - for (i = 0, len = jsactionRules.length; i < len; i++) { - jsactionRule = jsactionRules[i]; - ruleSplit = jsactionRule.trim().split(":"); - if ((1 <= (ref7 = ruleSplit.length) && ref7 <= 2)) { - ref8 = ruleSplit.length === 1 ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), ["_"]) : [ruleSplit[0]].concat(slice.call(ruleSplit[1].trim().split(".")), ["_"]), eventType = ref8[0], namespace = ref8[1], actionName = ref8[2]; - isClickable || (isClickable = eventType === "click" && namespace !== "none" && actionName !== "_"); + } + if ( + (ref1 = + (ref2 = element.getAttribute("aria-hidden")) != null + ? ref2.toLowerCase() + : void 0) === "" || + ref1 === "true" || + ((ref3 = + (ref4 = element.getAttribute("aria-disabled")) != null + ? ref4.toLowerCase() + : void 0) === "" || + ref3 === "true") + ) { + return []; + } + if (this.checkForAngularJs == null) { + this.checkForAngularJs = (function() { + var angularElements, + i, + k, + len, + len1, + ngAttributes, + prefix, + ref5, + ref6, + separator; + angularElements = document.getElementsByClassName("ng-scope"); + if (angularElements.length === 0) { + return function() { + return false; + }; + } else { + ngAttributes = []; + ref5 = ["", "data-", "x-"]; + for (i = 0, len = ref5.length; i < len; i++) { + prefix = ref5[i]; + ref6 = ["-", ":", "_"]; + for (k = 0, len1 = ref6.length; k < len1; k++) { + separator = ref6[k]; + ngAttributes.push(prefix + "ng" + separator + "click"); + } } + return function(element) { + var attribute, l, len2; + for (l = 0, len2 = ngAttributes.length; l < len2; l++) { + attribute = ngAttributes[l]; + if (element.hasAttribute(attribute)) { + return true; + } + } + return false; + }; } - } - switch (tagName) { - case "a": - isClickable = true; - break; - case "textarea": - isClickable || (isClickable = !element.disabled && !element.readOnly); - break; - case "input": - isClickable || (isClickable = !(((ref9 = element.getAttribute("type")) != null ? ref9.toLowerCase() : void 0) === "hidden" || element.disabled || (element.readOnly && DomUtils.isSelectable(element)))); - break; - case "button": - case "select": - isClickable || (isClickable = !element.disabled); - break; - case "label": - isClickable || (isClickable = (element.control != null) && !element.control.disabled && (this.getVisibleClickable(element.control)).length === 0); - break; - case "body": - isClickable || (isClickable = element === document.body && !windowIsFocused() && window.innerWidth > 3 && window.innerHeight > 3 && ((ref10 = document.body) != null ? ref10.tagName.toLowerCase() : void 0) !== "frameset" ? reason = "Frame." : void 0); - //isClickable || (isClickable = element === document.body && windowIsFocused() && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); - break; - case "img": - isClickable || (isClickable = (ref11 = element.style.cursor) === "zoom-in" || ref11 === "zoom-out"); - break; - case "div": - case "ol": - case "ul": - //isClickable || (isClickable = element.clientHeight < element.scrollHeight && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); - break; - case "details": - isClickable = true; - reason = "Open."; - } - if (!isClickable && 0 <= ((ref12 = element.getAttribute("class")) != null ? ref12.toLowerCase().indexOf("button") : void 0)) { - possibleFalsePositive = isClickable = true; - } - tabIndexValue = element.getAttribute("tabindex"); - tabIndex = tabIndexValue === "" ? 0 : parseInt(tabIndexValue); - if (!(isClickable || isNaN(tabIndex) || tabIndex < 0)) { - isClickable = onlyHasTabIndex = true; - } - if (isClickable) { - clientRect = DomUtils.getVisibleClientRect(element, true); - if (clientRect !== null) { - visibleElements.push({ - element: element, - rect: clientRect, - secondClassCitizen: onlyHasTabIndex, - possibleFalsePositive: possibleFalsePositive, - reason: reason - }); + })(); + } + isClickable || (isClickable = this.checkForAngularJs(element)); + if ( + element.hasAttribute("onclick") || + ((role = element.getAttribute("role")) && + ((ref5 = role.toLowerCase()) === "button" || + ref5 === "tab" || + ref5 === "link" || + ref5 === "checkbox" || + ref5 === "menuitem" || + ref5 === "menuitemcheckbox" || + ref5 === "menuitemradio")) || + ((contentEditable = element.getAttribute("contentEditable")) && + ((ref6 = contentEditable.toLowerCase()) === "" || + ref6 === "contenteditable" || + ref6 === "true")) + ) { + isClickable = true; + } + if (!isClickable && element.hasAttribute("jsaction")) { + jsactionRules = element.getAttribute("jsaction").split(";"); + for (i = 0, len = jsactionRules.length; i < len; i++) { + jsactionRule = jsactionRules[i]; + ruleSplit = jsactionRule.trim().split(":"); + if (1 <= (ref7 = ruleSplit.length) && ref7 <= 2) { + (ref8 = + ruleSplit.length === 1 + ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), [ + "_" + ]) + : [ruleSplit[0]].concat( + slice.call(ruleSplit[1].trim().split(".")), + ["_"] + )), + (eventType = ref8[0]), + (namespace = ref8[1]), + (actionName = ref8[2]); + isClickable || + (isClickable = + eventType === "click" && + namespace !== "none" && + actionName !== "_"); } } - return visibleElements; - }, - getLocalHints: function(requireHref) { - var descendantsToCheck, element, elements, hint, i, k, l, left, len, len1, len2, len3, localHints, m, negativeRect, nonOverlappingElements, position, rects, ref, ref1, top, visibleElement, visibleElements; - if (!document.documentElement) { - return []; + } + switch (tagName) { + case "a": + isClickable = true; + break; + case "textarea": + isClickable || (isClickable = !element.disabled && !element.readOnly); + break; + case "input": + isClickable || + (isClickable = !( + ((ref9 = element.getAttribute("type")) != null + ? ref9.toLowerCase() + : void 0) === "hidden" || + element.disabled || + (element.readOnly && DomUtils.isSelectable(element)) + )); + break; + case "button": + case "select": + isClickable || (isClickable = !element.disabled); + break; + case "label": + isClickable || + (isClickable = + element.control != null && + !element.control.disabled && + this.getVisibleClickable(element.control).length === 0); + break; + case "body": + isClickable || + (isClickable = + element === document.body && + !windowIsFocused() && + window.innerWidth > 3 && + window.innerHeight > 3 && + ((ref10 = document.body) != null + ? ref10.tagName.toLowerCase() + : void 0) !== "frameset" + ? (reason = "Frame.") + : void 0); + //isClickable || (isClickable = element === document.body && windowIsFocused() && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "img": + isClickable || + (isClickable = + (ref11 = element.style.cursor) === "zoom-in" || + ref11 === "zoom-out"); + break; + case "div": + case "ol": + case "ul": + //isClickable || (isClickable = element.clientHeight < element.scrollHeight && Scroller.isScrollableElement(element) ? reason = "Scroll." : void 0); + break; + case "details": + isClickable = true; + reason = "Open."; + } + if ( + !isClickable && + 0 <= + ((ref12 = element.getAttribute("class")) != null + ? ref12.toLowerCase().indexOf("button") + : void 0) + ) { + possibleFalsePositive = isClickable = true; + } + tabIndexValue = element.getAttribute("tabindex"); + tabIndex = tabIndexValue === "" ? 0 : parseInt(tabIndexValue); + if (!(isClickable || isNaN(tabIndex) || tabIndex < 0)) { + isClickable = onlyHasTabIndex = true; + } + if (isClickable) { + clientRect = DomUtils.getVisibleClientRect(element, true); + if (clientRect !== null) { + visibleElements.push({ + element: element, + rect: clientRect, + secondClassCitizen: onlyHasTabIndex, + possibleFalsePositive: possibleFalsePositive, + reason: reason + }); } - elements = document.documentElement.getElementsByTagName("*"); - visibleElements = []; - for (i = 0, len = elements.length; i < len; i++) { - element = elements[i]; - if (!(requireHref && !element.href)) { - visibleElement = this.getVisibleClickable(element); - if (requireHref && element.href && visibleElement.length > 0) { - visibleElement[0]["href"] = element.href; - } - visibleElements.push.apply(visibleElements, visibleElement); + } + return visibleElements; + }, + getLocalHints: function(requireHref) { + var descendantsToCheck, + element, + elements, + hint, + i, + k, + l, + left, + len, + len1, + len2, + len3, + localHints, + m, + negativeRect, + nonOverlappingElements, + position, + rects, + ref, + ref1, + top, + visibleElement, + visibleElements; + if (!document.documentElement) { + return []; + } + elements = document.documentElement.getElementsByTagName("*"); + visibleElements = []; + for (i = 0, len = elements.length; i < len; i++) { + element = elements[i]; + if (!(requireHref && !element.href)) { + visibleElement = this.getVisibleClickable(element); + if (requireHref && element.href && visibleElement.length > 0) { + visibleElement[0]["href"] = element.href; } + visibleElements.push.apply(visibleElements, visibleElement); } - visibleElements = visibleElements.reverse(); - descendantsToCheck = [1, 2, 3]; - visibleElements = (function() { - var k, len1, results; - results = []; - for (position = k = 0, len1 = visibleElements.length; k < len1; position = ++k) { - element = visibleElements[position]; - if (element.possibleFalsePositive && (function() { + } + visibleElements = visibleElements.reverse(); + descendantsToCheck = [1, 2, 3]; + visibleElements = (function() { + var k, len1, results; + results = []; + for ( + position = k = 0, len1 = visibleElements.length; + k < len1; + position = ++k + ) { + element = visibleElements[position]; + if ( + element.possibleFalsePositive && + (function() { var _, candidateDescendant, index, l, len2; index = Math.max(0, position - 6); while (index < position) { candidateDescendant = visibleElements[index].element; for (l = 0, len2 = descendantsToCheck.length; l < len2; l++) { _ = descendantsToCheck[l]; - candidateDescendant = candidateDescendant != null ? candidateDescendant.parentElement : void 0; + candidateDescendant = + candidateDescendant != null + ? candidateDescendant.parentElement + : void 0; if (candidateDescendant === element.element) { return true; } @@ -530,192 +851,288 @@ var DomUtils = { index += 1; } return false; - })()) { - continue; - } - results.push(element); + })() + ) { + continue; } - return results; - })(); - localHints = nonOverlappingElements = []; - while (visibleElement = visibleElements.pop()) { - rects = [visibleElement.rect]; - for (k = 0, len1 = visibleElements.length; k < len1; k++) { - negativeRect = visibleElements[k].rect; - rects = (ref = []).concat.apply(ref, rects.map(function(rect) { + results.push(element); + } + return results; + })(); + localHints = nonOverlappingElements = []; + while ((visibleElement = visibleElements.pop())) { + rects = [visibleElement.rect]; + for (k = 0, len1 = visibleElements.length; k < len1; k++) { + negativeRect = visibleElements[k].rect; + rects = (ref = []).concat.apply( + ref, + rects.map(function(rect) { return Rect.subtract(rect, negativeRect); - })); + }) + ); + } + if (rects.length > 0) { + try { + var testextend = extend(visibleElement, { rect: rects[0] }); + nonOverlappingElements.push(testextend); + } catch (error) { + nonOverlappingElements.push(visibleElement); } - if (rects.length > 0) { - try { - var testextend = extend(visibleElement, { rect: rects[0] }); - nonOverlappingElements.push(testextend); - } catch(error) { - nonOverlappingElements.push(visibleElement); - } - /*nonOverlappingElements.push(extend(visibleElement, { + /*nonOverlappingElements.push(extend(visibleElement, { rect: rects[0] }));*/ - } else { - if (!visibleElement.secondClassCitizen) { - nonOverlappingElements.push(visibleElement); - } + } else { + if (!visibleElement.secondClassCitizen) { + nonOverlappingElements.push(visibleElement); } - } - ref1 = DomUtils.getViewportTopLeft(), top = ref1.top, left = ref1.left; - for (l = 0, len2 = nonOverlappingElements.length; l < len2; l++) { - hint = nonOverlappingElements[l]; - hint.rect.top += top; - hint.rect.left += left; } - /*if (Settings.get("filterLinkHints")) { + } + (ref1 = DomUtils.getViewportTopLeft()), + (top = ref1.top), + (left = ref1.left); + for (l = 0, len2 = nonOverlappingElements.length; l < len2; l++) { + hint = nonOverlappingElements[l]; + hint.rect.top += top; + hint.rect.left += left; + } + /*if (Settings.get("filterLinkHints")) { for (m = 0, len3 = localHints.length; m < len3; m++) { hint = localHints[m]; extend(hint, this.generateLinkText(hint)); } }*/ - return localHints; - }, - generateLinkText: function(hint) { - var element, linkText, nodeName, ref, showLinkText; - element = hint.element; - linkText = ""; - showLinkText = false; - nodeName = element.nodeName.toLowerCase(); - if (nodeName === "input") { - if ((element.labels != null) && element.labels.length > 0) { - linkText = element.labels[0].textContent.trim(); - if (linkText[linkText.length - 1] === ":") { - linkText = linkText.slice(0, linkText.length - 1); - } - showLinkText = true; - } else if (((ref = element.getAttribute("type")) != null ? ref.toLowerCase() : void 0) === "file") { - linkText = "Choose File"; - } else if (element.type !== "password") { - linkText = element.value; - if (!linkText && 'placeholder' in element) { - linkText = element.placeholder; - } + return localHints; + }, + generateLinkText: function(hint) { + var element, linkText, nodeName, ref, showLinkText; + element = hint.element; + linkText = ""; + showLinkText = false; + nodeName = element.nodeName.toLowerCase(); + if (nodeName === "input") { + if (element.labels != null && element.labels.length > 0) { + linkText = element.labels[0].textContent.trim(); + if (linkText[linkText.length - 1] === ":") { + linkText = linkText.slice(0, linkText.length - 1); } - } else if (nodeName === "a" && !element.textContent.trim() && element.firstElementChild && element.firstElementChild.nodeName.toLowerCase() === "img") { - linkText = element.firstElementChild.alt || element.firstElementChild.title; - if (linkText) { - showLinkText = true; + showLinkText = true; + } else if ( + ((ref = element.getAttribute("type")) != null + ? ref.toLowerCase() + : void 0) === "file" + ) { + linkText = "Choose File"; + } else if (element.type !== "password") { + linkText = element.value; + if (!linkText && "placeholder" in element) { + linkText = element.placeholder; } - } else if (hint.reason != null) { - linkText = hint.reason; + } + } else if ( + nodeName === "a" && + !element.textContent.trim() && + element.firstElementChild && + element.firstElementChild.nodeName.toLowerCase() === "img" + ) { + linkText = + element.firstElementChild.alt || element.firstElementChild.title; + if (linkText) { showLinkText = true; - } else if (0 < element.textContent.length) { - linkText = element.textContent.slice(0, 256); - } else if (element.hasAttribute("title")) { - linkText = element.getAttribute("title"); - } else { - linkText = element.innerHTML.slice(0, 256); } - return { - linkText: linkText.trim(), - showLinkText: showLinkText - }; + } else if (hint.reason != null) { + linkText = hint.reason; + showLinkText = true; + } else if (0 < element.textContent.length) { + linkText = element.textContent.slice(0, 256); + } else if (element.hasAttribute("title")) { + linkText = element.getAttribute("title"); + } else { + linkText = element.innerHTML.slice(0, 256); } - }; + return { + linkText: linkText.trim(), + showLinkText: showLinkText + }; + } +}; var VimiumNormal = { - followLink : function(linkElement) { - if (linkElement.nodeName.toLowerCase() === "link") { - return window.location.href = linkElement.href; - } else { - linkElement.scrollIntoView(); - return DomUtils.simulateClick(linkElement); - } - }, - findAndFollowLink : function(linkStrings) { - var boundingClientRect, candidateLink, candidateLinks, computedStyle, exactWordRegex, i, j, k, l, len, len1, len2, len3, link, linkMatches, linkString, links, linksXPath, m, n, ref, ref1; - linksXPath = DomUtils.makeXPath(["a", "*[@onclick or @role='link' or contains(@class, 'button')]"]); - links = DomUtils.evaluateXPath(linksXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); - candidateLinks = []; - for (i = j = ref = links.snapshotLength - 1; j >= 0; i = j += -1) { - link = links.snapshotItem(i); - boundingClientRect = link.getBoundingClientRect(); - if (boundingClientRect.width === 0 || boundingClientRect.height === 0) { - continue; - } - computedStyle = window.getComputedStyle(link, null); - if (computedStyle.getPropertyValue("visibility") !== "visible" || computedStyle.getPropertyValue("display") === "none") { - continue; - } - linkMatches = false; - for (k = 0, len = linkStrings.length; k < len; k++) { - linkString = linkStrings[k]; - if (link.innerText.toLowerCase().indexOf(linkString) !== -1 || 0 <= ((ref1 = link.value) != null ? typeof ref1.indexOf === "function" ? ref1.indexOf(linkString) : void 0 : void 0)) { - linkMatches = true; - break; - } - } - if (!linkMatches) { - continue; - } - candidateLinks.push(link); - } - if (candidateLinks.length === 0) { - return; - } - for (l = 0, len1 = candidateLinks.length; l < len1; l++) { - link = candidateLinks[l]; - link.wordCount = link.innerText.trim().split(/\s+/).length; - } - candidateLinks.forEach(function(a, i) { - return a.originalIndex = i; - }); - candidateLinks = candidateLinks.sort(function(a, b) { - if (a.wordCount === b.wordCount) { - return a.originalIndex - b.originalIndex; - } else { - return a.wordCount - b.wordCount; - } - }).filter(function(a) { - return a.wordCount <= candidateLinks[0].wordCount + 1; - }); - for (m = 0, len2 = linkStrings.length; m < len2; m++) { - linkString = linkStrings[m]; - exactWordRegex = /\b/.test(linkString[0]) || /\b/.test(linkString[linkString.length - 1]) ? new RegExp("\\b" + linkString + "\\b", "i") : new RegExp(linkString, "i"); - for (n = 0, len3 = candidateLinks.length; n < len3; n++) { - candidateLink = candidateLinks[n]; - if (exactWordRegex.test(candidateLink.innerText) || (candidateLink.value && exactWordRegex.test(candidateLink.value))) { - this.followLink(candidateLink); - return true; - } - } - } - return false; - }, - findAndFollowRel : function(value) { - var element, elements, j, k, len, len1, relTags, tag; - relTags = ["link", "a", "area"]; - for (j = 0, len = relTags.length; j < len; j++) { - tag = relTags[j]; - elements = document.getElementsByTagName(tag); - for (k = 0, len1 = elements.length; k < len1; k++) { - element = elements[k]; - if (element.hasAttribute("rel") && element.rel.toLowerCase() === value) { - this.followLink(element); - return true; - } - } - } + followLink: function(linkElement) { + if (linkElement.nodeName.toLowerCase() === "link") { + return (window.location.href = linkElement.href); + } else { + linkElement.scrollIntoView(); + return DomUtils.simulateClick(linkElement); + } + }, + findAndFollowLink: function(linkStrings) { + var boundingClientRect, + candidateLink, + candidateLinks, + computedStyle, + exactWordRegex, + i, + j, + k, + l, + len, + len1, + len2, + len3, + link, + linkMatches, + linkString, + links, + linksXPath, + m, + n, + ref, + ref1; + linksXPath = DomUtils.makeXPath([ + "a", + "*[@onclick or @role='link' or contains(@class, 'button')]" + ]); + links = DomUtils.evaluateXPath( + linksXPath, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + ); + candidateLinks = []; + for (i = j = ref = links.snapshotLength - 1; j >= 0; i = j += -1) { + link = links.snapshotItem(i); + boundingClientRect = link.getBoundingClientRect(); + if (boundingClientRect.width === 0 || boundingClientRect.height === 0) { + continue; + } + computedStyle = window.getComputedStyle(link, null); + if ( + computedStyle.getPropertyValue("visibility") !== "visible" || + computedStyle.getPropertyValue("display") === "none" + ) { + continue; + } + linkMatches = false; + for (k = 0, len = linkStrings.length; k < len; k++) { + linkString = linkStrings[k]; + if ( + link.innerText.toLowerCase().indexOf(linkString) !== -1 || + 0 <= + ((ref1 = link.value) != null + ? typeof ref1.indexOf === "function" + ? ref1.indexOf(linkString) + : void 0 + : void 0) + ) { + linkMatches = true; + break; + } + } + if (!linkMatches) { + continue; + } + candidateLinks.push(link); + } + if (candidateLinks.length === 0) { + return; + } + for (l = 0, len1 = candidateLinks.length; l < len1; l++) { + link = candidateLinks[l]; + link.wordCount = link.innerText.trim().split(/\s+/).length; + } + candidateLinks.forEach(function(a, i) { + return (a.originalIndex = i); + }); + candidateLinks = candidateLinks + .sort(function(a, b) { + if (a.wordCount === b.wordCount) { + return a.originalIndex - b.originalIndex; + } else { + return a.wordCount - b.wordCount; + } + }) + .filter(function(a) { + return a.wordCount <= candidateLinks[0].wordCount + 1; + }); + for (m = 0, len2 = linkStrings.length; m < len2; m++) { + linkString = linkStrings[m]; + exactWordRegex = + /\b/.test(linkString[0]) || /\b/.test(linkString[linkString.length - 1]) + ? new RegExp("\\b" + linkString + "\\b", "i") + : new RegExp(linkString, "i"); + for (n = 0, len3 = candidateLinks.length; n < len3; n++) { + candidateLink = candidateLinks[n]; + if ( + exactWordRegex.test(candidateLink.innerText) || + (candidateLink.value && exactWordRegex.test(candidateLink.value)) + ) { + this.followLink(candidateLink); + return true; + } + } + } + return false; + }, + findAndFollowRel: function(value) { + var element, elements, j, k, len, len1, relTags, tag; + relTags = ["link", "a", "area"]; + for (j = 0, len = relTags.length; j < len; j++) { + tag = relTags[j]; + elements = document.getElementsByTagName(tag); + for (k = 0, len1 = elements.length; k < len1; k++) { + element = elements[k]; + if ( + element.hasAttribute("rel") && + element.rel.toLowerCase() === value + ) { + this.followLink(element); + return true; + } + } + } + }, + textInputXPath: function() { + var inputElements, textInputTypes; + textInputTypes = [ + "text", + "search", + "email", + "url", + "number", + "password", + "date", + "tel" + ]; + inputElements = [ + "input[" + + "(" + + textInputTypes + .map(function(type) { + return '@type="' + type + '"'; + }) + .join(" or ") + + "or not(@type))" + + " and not(@disabled or @readonly)]", + "textarea", + "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" + ]; + return typeof DomUtils !== "undefined" && DomUtils !== null + ? DomUtils.makeXPath(inputElements) + : void 0; }, - textInputXPath : function() { - var inputElements, textInputTypes; - textInputTypes = ["text", "search", "email", "url", "number", "password", "date", "tel"]; - inputElements = [ - "input[" + "(" + textInputTypes.map(function(type) { - return '@type="' + type + '"'; - }).join(" or ") + "or not(@type))" + " and not(@disabled or @readonly)]", "textarea", "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" - ]; - return typeof DomUtils !== "undefined" && DomUtils !== null ? DomUtils.makeXPath(inputElements) : void 0; - }, focusInput: function(count) { - var element, elements, hint, hints, i, recentlyFocusedElement, resultSet, selectedInputIndex, tuple, visibleInputs; - resultSet = DomUtils.evaluateXPath(textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE); + var element, + elements, + hint, + hints, + i, + recentlyFocusedElement, + resultSet, + selectedInputIndex, + tuple, + visibleInputs; + resultSet = DomUtils.evaluateXPath( + textInputXPath, + XPathResult.ORDERED_NODE_SNAPSHOT_TYPE + ); visibleInputs = (function() { var j, ref, results; results = []; @@ -734,8 +1151,8 @@ var VimiumNormal = { })(); visibleInputs.sort(function(arg, arg1) { var element1, element2, i1, i2, tabDifference; - element1 = arg.element, i1 = arg.index; - element2 = arg1.element, i2 = arg1.index; + (element1 = arg.element), (i1 = arg.index); + (element2 = arg1.element), (i2 = arg1.index); if (element1.tabIndex > 0) { if (element2.tabIndex > 0) { tabDifference = element1.tabIndex - element2.tabIndex; @@ -758,9 +1175,13 @@ var VimiumNormal = { return; } recentlyFocusedElement = lastFocusedInput(); - selectedInputIndex = count === 1 ? (elements = visibleInputs.map(function(visibleInput) { - return visibleInput.element; - }), Math.max(0, elements.indexOf(recentlyFocusedElement))) : Math.min(count, visibleInputs.length) - 1; + selectedInputIndex = + count === 1 + ? ((elements = visibleInputs.map(function(visibleInput) { + return visibleInput.element; + })), + Math.max(0, elements.indexOf(recentlyFocusedElement))) + : Math.min(count, visibleInputs.length) - 1; hints = (function() { var j, len, results; results = []; @@ -768,8 +1189,8 @@ var VimiumNormal = { tuple = visibleInputs[j]; hint = DomUtils.createElement("div"); hint.className = "vimiumReset internalVimiumInputHint vimiumInputHint"; - hint.style.left = (tuple.rect.left - 1) + window.scrollX + "px"; - hint.style.top = (tuple.rect.top - 1) + window.scrollY + "px"; + hint.style.left = tuple.rect.left - 1 + window.scrollX + "px"; + hint.style.top = tuple.rect.top - 1 + window.scrollY + "px"; hint.style.width = tuple.rect.width + "px"; hint.style.height = tuple.rect.height + "px"; results.push(hint); @@ -778,8 +1199,7 @@ var VimiumNormal = { })(); return new FocusSelector(hints, visibleInputs, selectedInputIndex); } - }; - +}; export function MiscVimium() { if (window.forTrusted == null) { @@ -797,29 +1217,33 @@ export function MiscVimium() { var windowHasFocus; windowHasFocus = null; DomUtils.documentReady(function() { - return windowHasFocus = document.hasFocus(); + return (windowHasFocus = document.hasFocus()); }); - window.addEventListener("focus", forTrusted(function(event) { - if (event.target === window) { - windowHasFocus = true; - } - return true; - })); - window.addEventListener("blur", forTrusted(function(event) { - if (event.target === window) { - windowHasFocus = false; - } - return true; - })); + window.addEventListener( + "focus", + forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = true; + } + return true; + }) + ); + window.addEventListener( + "blur", + forTrusted(function(event) { + if (event.target === window) { + windowHasFocus = false; + } + return true; + }) + ); return function() { return windowHasFocus; }; }; -}; - -export { Rect } -export { DomUtils } -export { LocalHints } -export { VimiumNormal } - +} +export { Rect }; +export { DomUtils }; +export { LocalHints }; +export { VimiumNormal }; From 49eebee0c93fcf75a753184d7c10c7cbed23f424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Mon, 29 Oct 2018 11:38:26 -0400 Subject: [PATCH 65/77] Fixed typo --- interfacer/test/tty/tty_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index f31f2445..8c620c40 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -191,7 +191,7 @@ var _ = Describe("Showing a basic webpage", func() { }) Describe("Text positioning", func() { - It("should position the left/right-aligned coloumns", func() { + It("should position the left/right-aligned columns", func() { Expect("Smörgåsbord▄(Swedish:").To(BeInFrameAt(12, 10)) Expect("The▄Swedish▄word").To(BeInFrameAt(42, 10)) }) From e03923394c5697c89cb2fee8dd218b561b9ab292 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 9 Nov 2018 16:43:35 +0900 Subject: [PATCH 66/77] Refactors Vim code from tty.go into its own file --- interfacer/src/browsh/tty.go | 593 +---------------------------- interfacer/src/browsh/vim_mode.go | 601 ++++++++++++++++++++++++++++++ 2 files changed, 606 insertions(+), 588 deletions(-) create mode 100644 interfacer/src/browsh/vim_mode.go diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index 098b447c..f7663b5e 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -4,110 +4,26 @@ import ( "encoding/json" "fmt" "os" - "reflect" "strconv" - "strings" "time" - "unicode" - "github.com/atotto/clipboard" "github.com/gdamore/tcell" "github.com/go-errors/errors" "github.com/spf13/viper" ) -type VimMode int - -const ( - NormalMode VimMode = iota + 1 - InsertMode - FindMode - LinkMode - LinkModeNewTab - LinkModeCopy - WaitMode - VisualMode - CaretMode - MakeMarkMode - GotoMarkMode -) - -type mark struct { - tabID int - URI string - xScroll int - yScroll int -} - type Coordinate struct { X, Y int } -type HintRect struct { - Bottom int `json:"bottom"` - Top int `json:"top"` - Left int `json:"left"` - Right int `json:"right"` - Width int `json:"width"` - Height int `json:"height"` - Href string `json:"href"` -} - var ( - screen tcell.Screen + screen tcell.Screen + // The height of the tabs and URL bar uiHeight = 2 // IsMonochromeMode decides whether to render the TTY in full colour or monochrome IsMonochromeMode = false - // vimMode - vimMode = NormalMode - vimCommandsBindings = make(map[string]string) - keyEvents = make([]*tcell.EventKey, 11) - //runeTime = make(map[rune]time.Time) - //lastRune rune - waitModeStartTime time.Time - findText string - // marks - globalMarkMap = make(map[rune]*mark) - localMarkMap = make(map[int]map[rune]*mark) - // position coordinate for caret mode - caretPos Coordinate - // for link modes - linkText string - linkHintRects []HintRect - linkHintKeys = "asdfwerxcv" - linkHints []string - linkHintsToRects = make(map[string]*HintRect) - linkModeWithHints = true - linkHintWriteStringCalls *func() ) -func init() { - setupLinkHints() -} - -func setupLinkHints() { - lowerAlpha := "abcdefghijklmnopqrstuvwxyz" - missingAlpha := lowerAlpha - - // Use linkHintKeys first to generate link hints - for i := 0; i < len(linkHintKeys); i++ { - for j := 0; j < len(linkHintKeys); j++ { - linkHints = append(linkHints, string(linkHintKeys[i])+string(linkHintKeys[j])) - } - missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) - } - - // missingAlpha contains all keys that aren't in linkHintKeys - // we use this to generate the last link hint key combinations, - // so this will only be used when we run out of linkHintKeys based - // link hint key combinations. - for i := 0; i < len(missingAlpha); i++ { - for j := 0; j < len(lowerAlpha); j++ { - linkHints = append(linkHints, string(missingAlpha[i])+string(lowerAlpha[j])) - } - } -} - func setupTcell() { var err error if err = screen.Init(); err != nil { @@ -183,6 +99,7 @@ func handleUserKeyPress(ev *tcell.EventKey) { } } +// Matches a human-readable key defintion with a Tcell event func isKey(userKey string, ev *tcell.EventKey) bool { key := viper.GetStringSlice(userKey) runeMatch := []rune(key[0])[0] == ev.Rune() @@ -230,69 +147,6 @@ func isMultiLineEnter(ev *tcell.EventKey) bool { return activeInputBox.isMultiLine() && ev.Key() == 13 && ev.Modifiers() != 4 } -// Moves the caret in CaretMode. -// testFunc is a function that tests for the reaching of the boundaries of the given axis. -// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. -// The step size and direction is given by the value of step. -func moveVimCaret(testFunc func() bool, valRef *int, step int) { - var prevCell, nextCell, nextNextCell cell - var r rune - hasNextNextCell := false - - for testFunc() { - prevCell = getCell(caretPos.X, caretPos.Y-uiHeight) - *valRef += step - nextCell = getCell(caretPos.X, caretPos.Y-uiHeight) - - if testFunc() { - *valRef += step - nextNextCell = getCell(caretPos.X, caretPos.Y-uiHeight) - *valRef -= step - hasNextNextCell = true - } else { - hasNextNextCell = false - } - - r = nextCell.character[0] - // Check if the next cell is different in any way - if !reflect.DeepEqual(prevCell, nextCell) { - if hasNextNextCell { - // This condition should apply to the spaces between words and the like - // Checking with unicode.isSpace() didn't give correct results for some reason - // TODO: find out what that reason is and improve this - if !unicode.IsLetter(r) && unicode.IsLetter(nextNextCell.character[0]) { - continue - } - // If the upcoming cell is deeply equal we can continue to go forward - if reflect.DeepEqual(nextCell, nextNextCell) { - continue - } - } - // This cell is different and other conditions for continuing don't apply - // therefore we stop going forward. - break - } - } -} - -// TODO: This fails if the tab with mark.tabID doesn't exist anymore it should recreate said tab, then go to the mark's URL and position -func gotoMark(mark *mark) { - if CurrentTab.ID != mark.tabID { - ensureTabExists(mark.tabID) - switchToTab(mark.tabID) - } - if CurrentTab.URI != mark.URI { - sendMessageToWebExtension("/tab_command,/url," + mark.URI) - //sleep? - } - doScrollAbsolute(mark.xScroll, mark.yScroll) -} - -// Make a mark at the current position in the current tab -func makeMark() *mark { - return &mark{CurrentTab.ID, CurrentTab.URI, CurrentTab.frame.xScroll, CurrentTab.frame.yScroll} -} - func generateLeftClickYHack(x, y int, yHack bool) { newMouseEvent := tcell.NewEventMouse(x, y+uiHeight, tcell.Button1, 0) handleMouseEventYHack(newMouseEvent, yHack) @@ -314,358 +168,6 @@ func generateMiddleClick(x, y int) { handleMouseEvent(newMouseEvent) } -func goIntoWaitMode() { - vimMode = WaitMode - waitModeStartTime = time.Now() -} - -func updateLinkHintDisplay() { - linkHintsToRects = make(map[string]*HintRect) - lh := len(linkHintRects) - var ht string - // List of closures - var fc []*func() - - for i, r := range linkHintRects { - // When the number of link hints is small enough - // using just one key for individual link hints suffices. - // Otherwise use the prepared link hint key combinations. - if lh <= len(linkHintKeys) { - ht = string(linkHintKeys[i]) - } else { - ht = linkHints[i] - } - // Add the key combination ht to the linkHintsToRects map. - // When the user presses it, we can easily lookup the - // link hint properties associated with it. - linkHintsToRects[ht] = &linkHintRects[i] - - // When the first key got hit, - // shorten the link hints accordingly - offsetLeft := 0 - if strings.HasPrefix(ht, linkText) { - ht = ht[len(linkText):len(ht)] - offsetLeft = len(linkText) - } - - // Make copies of parameter values - rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht - - // Link hints are in upper case in new tab mode - if vimMode == LinkModeNewTab { - htCopy = strings.ToUpper(htCopy) - } - - // Create closure - f := func() { - writeString(rLeftCopy+offsetLeft, rTopCopy+uiHeight, htCopy, tcell.StyleDefault) - } - fc = append(fc, &f) - } - // Create closure that calls the other closures - ff := func() { - for _, f := range fc { - (*f)() - } - } - linkHintWriteStringCalls = &ff -} - -func eraseLinkHints() { - linkText = "" - linkHintWriteStringCalls = nil - linkHintsToRects = make(map[string]*HintRect) - linkHintRects = nil -} - -func isNormalModeKey(ev *tcell.EventKey) bool { - if ev.Key() == tcell.KeyESC { - return true - } - return false -} - -func handleVimControl(ev *tcell.EventKey) { - var lastRune rune - command := "" - - if len(keyEvents) > 0 && keyEvents[0] != nil { - lastRune = keyEvents[len(keyEvents)-1].Rune() - } - - keyEvents = append(keyEvents, ev) - if len(keyEvents) > 10 { - keyEvents = keyEvents[1:] - } - - keyCombination := string(lastRune) + string(ev.Rune()) - - switch vimMode { - case WaitMode: - if time.Since(waitModeStartTime) < time.Millisecond*1000 { - return - } - vimMode = NormalMode - fallthrough - case NormalMode: - command = vimCommandsBindings[keyCombination] - if len(command) == 0 { - keyCombination := string(ev.Rune()) - command = vimCommandsBindings[keyCombination] - } - case InsertMode: - if isNormalModeKey(ev) { - command = "normalMode" - } - case VisualMode: - if isNormalModeKey(ev) { - command = "normalMode" - } else { - if ev.Rune() == 'c' { - command = "caretMode" - } - if ev.Rune() == 'o' { - //swap cursor position begin->end or end->begin - } - if ev.Rune() == 'y' { - //clipboard - } - } - case CaretMode: - if isNormalModeKey(ev) { - command = "normalMode" - } else { - switch ev.Key() { - case tcell.KeyEnter: - generateLeftClick(caretPos.X, caretPos.Y-uiHeight) - } - switch ev.Rune() { - case 'v': - command = "visualMode" - case 'h': - moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) - case 'l': - width, _ := screen.Size() - moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) - case 'k': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) - if caretPos.Y < uiHeight { - command = "scrollHalfPageUp" - if CurrentTab.frame.yScroll == 0 { - caretPos.Y = uiHeight - } else { - caretPos.Y += (height - uiHeight) / 2 - } - } - case 'j': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) - if caretPos.Y > height-uiHeight { - command = "scrollHalfPageDown" - caretPos.Y -= (height - uiHeight) / 2 - } - } - } - case MakeMarkMode: - if unicode.IsLower(ev.Rune()) { - if localMarkMap[CurrentTab.ID] == nil { - localMarkMap[CurrentTab.ID] = make(map[rune]*mark) - } - localMarkMap[CurrentTab.ID][ev.Rune()] = makeMark() - } else if unicode.IsUpper(ev.Rune()) { - globalMarkMap[ev.Rune()] = makeMark() - } - - command = "normalMode" - case GotoMarkMode: - if mark, ok := globalMarkMap[ev.Rune()]; ok { - gotoMark(mark) - } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { - if mark, ok := m[ev.Rune()]; ok { - gotoMark(mark) - } - } - - command = "normalMode" - case FindMode: - if isNormalModeKey(ev) { - command = "normalMode" - findText = "" - } else { - if ev.Key() == tcell.KeyEnter { - vimMode = NormalMode - command = "findText" - break - } - if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 { - if len(findText) > 0 { - findText = findText[:len(findText)-1] - } - } else { - findText += string(ev.Rune()) - } - } - case LinkMode, LinkModeNewTab, LinkModeCopy: - if isNormalModeKey(ev) { - command = "normalMode" - eraseLinkHints() - } else { - linkText += string(ev.Rune()) - updateLinkHintDisplay() - if linkModeWithHints { - if r, ok := linkHintsToRects[linkText]; ok { - if r != nil { - switch vimMode { - case LinkMode: - if (*r).Height == 2 { - generateLeftClickYHack((*r).Left, (*r).Top, true) - } else { - generateLeftClick((*r).Left, (*r).Top) - } - case LinkModeNewTab: - //generateMiddleClick(r.Left, r.Top) - sendMessageToWebExtension("/new_tab," + r.Href) - case LinkModeCopy: - clipboard.WriteAll(r.Href) - } - goIntoWaitMode() - eraseLinkHints() - } - } - } else { - coords := findAndHighlightTextOnScreen(linkText) - if len(coords) == 1 { - goIntoWaitMode() - - if vimMode == LinkModeNewTab { - generateMiddleClick(coords[0].X, coords[0].Y) - } else { - generateLeftClick(coords[0].X, coords[0].Y) - } - linkText = "" - return - } else if len(coords) == 0 { - vimMode = NormalMode - linkText = "" - return - } - } - } - } - - if len(command) > 0 { - executeVimCommand(command) - } -} - -func executeVimCommand(command string) { - switch command { - case "urlUp": - sendMessageToWebExtension("/tab_command,/url_up") - case "urlRoot": - sendMessageToWebExtension("/tab_command,/url_root") - case "scrollToTop": - doScroll(0, -CurrentTab.frame.domRowCount()) - case "scrollToBottom": - doScroll(0, CurrentTab.frame.domRowCount()) - case "scrollUp": - doScroll(0, -1) - case "scrollDown": - doScroll(0, 1) - case "scrollLeft": - doScroll(-1, 0) - case "scrollRight": - doScroll(1, 0) - case "editURL": - urlBarFocusToggle() - case "firstTab": - switchToTab(tabsOrder[0]) - case "lastTab": - switchToTab(tabsOrder[len(tabsOrder)-1]) - case "scrollHalfPageDown": - _, height := screen.Size() - doScroll(0, (height-uiHeight)/2) - case "scrollHalfPageUp": - _, height := screen.Size() - doScroll(0, -((height - uiHeight) / 2)) - case "historyBack": - sendMessageToWebExtension("/tab_command,/history_back") - case "historyForward": - sendMessageToWebExtension("/tab_command,/history_forward") - case "reload": - sendMessageToWebExtension("/tab_command,/reload") - case "prevTab": - prevTab() - case "nextTab": - nextTab() - case "previouslyVisitedTab": - previouslyVisitedTab() - case "newTab": - createNewEmptyTab() - case "removeTab": - removeTab(CurrentTab.ID) - case "restoreTab": - restoreTab() - case "duplicateTab": - duplicateTab(CurrentTab.ID) - case "moveTabLeft": - moveTabLeft(CurrentTab.ID) - case "moveTabRight": - moveTabRight(CurrentTab.ID) - case "copyURL": - clipboard.WriteAll(CurrentTab.URI) - case "openClipboardURL": - URI, _ := clipboard.ReadAll() - sendMessageToWebExtension("/tab_command,/url," + URI) - case "openClipboardURLInNewTab": - URI, _ := clipboard.ReadAll() - sendMessageToWebExtension("/new_tab," + URI) - case "focusFirstTextInput": - sendMessageToWebExtension("/tab_command,/focus_first_text_input") - case "followLinkLabeledNext": - sendMessageToWebExtension("/tab_command,/follow_link_labeled_next") - case "followLinkLabeledPrevious": - sendMessageToWebExtension("/tab_command,/follow_link_labeled_previous") - case "viewHelp": - sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") - case "openLinkInCurrentTab": - vimMode = LinkMode - sendMessageToWebExtension("/tab_command,/get_clickable_hints") - eraseLinkHints() - case "openLinkInNewTab": - vimMode = LinkModeNewTab - sendMessageToWebExtension("/tab_command,/get_link_hints") - eraseLinkHints() - case "copyLinkURL": - vimMode = LinkModeCopy - sendMessageToWebExtension("/tab_command,/get_link_hints") - eraseLinkHints() - case "findText": - fallthrough - case "findNext": - sendMessageToWebExtension("/tab_command,/find_next," + findText) - case "findPrevious": - sendMessageToWebExtension("/tab_command,/find_previous," + findText) - case "makeMark": - vimMode = MakeMarkMode - case "gotoMark": - vimMode = GotoMarkMode - case "insertMode": - vimMode = InsertMode - case "findMode": - vimMode = FindMode - case "normalMode": - vimMode = NormalMode - case "visualMode": - vimMode = VisualMode - case "caretMode": - vimMode = CaretMode - width, height := screen.Size() - caretPos.X, caretPos.Y = width/2, height/2 - } -} - func doScroll(relX int, relY int) { doScrollAbsolute(CurrentTab.frame.xScroll+relX, CurrentTab.frame.yScroll+relY) } @@ -795,93 +297,6 @@ func renderCurrentTabWindow() { screen.Show() } -func searchScreenForText(text string) []Coordinate { - var offsets = make([]Coordinate, 0) - var splitString []string - var r rune - var s string - width, height := screen.Size() - screenText := "" - index := 0 - - for y := 0; y < height-uiHeight; y++ { - screenText = "" - for x := 0; x < width; x++ { - r = getCell(x, y).character[0] - s = string(r) - if len(s) == 0 || len(s) > 1 { - screenText += " " - } else { - screenText += string(getCell(x, y).character[0]) - } - } - index = 0 - splitString = strings.Split(strings.ToLower(screenText), strings.ToLower(text)) - for _, s := range splitString { - if index+len(s) >= width { - break - } - - offsets = append(offsets, Coordinate{index + len(s), y}) - index += len(s) + len(text) - } - } - return offsets -} - -func findAndHighlightTextOnScreen(text string) []Coordinate { - var x, y int - var styling = tcell.StyleDefault - - offsets := searchScreenForText(text) - for _, offset := range offsets { - y = offset.Y - x = offset.X - for z := 0; z < len(text); z++ { - screen.SetContent(x+z, y+uiHeight, rune(text[z]), nil, styling) - } - } - screen.Show() - return offsets -} - -// Parse incoming link hints -func parseJSONLinkHints(jsonString string) { - jsonBytes := []byte(jsonString) - if err := json.Unmarshal(jsonBytes, &linkHintRects); err != nil { - Shutdown(err) - } - - // Optimize link hint positions - for i := 0; i < len(linkHintRects); i++ { - r := &linkHintRects[i] - - // For links that are more than one line high - // we want to position the link hint in the vertical middle - if r.Height > 2 { - if r.Height%2 == 0 { - r.Top += r.Height / 2 - } else { - r.Top += r.Height/2 - 1 - } - } - - // For links that are more one character long we try to move - // the link hint two characters to the right, if possible. - if r.Width > 1 { - o := r.Left - r.Left += r.Width/2 - 1 - if r.Left > o+2 { - r.Left = o + 2 - } - } - } - - Log("Received parseJSONLinkHint") - // This is where the display of actual link hints is prepared - updateLinkHintDisplay() -} - func getCell(x, y int) cell { var currentCell cell var ok bool @@ -898,6 +313,8 @@ func getCell(x, y int) cell { return currentCell } +// These are the dark and light grey squares that appear in the background to indicate that +// nothing has been rendered there yet. func getHatchedCellColours(x int) (tcell.Color, tcell.Color) { var bgColour, fgColour tcell.Color if x%2 == 0 { diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go new file mode 100644 index 00000000..cd3ad673 --- /dev/null +++ b/interfacer/src/browsh/vim_mode.go @@ -0,0 +1,601 @@ +package browsh + +import ( + "encoding/json" + "reflect" + "strings" + "time" + "unicode" + + "github.com/atotto/clipboard" + "github.com/gdamore/tcell" +) + +// TODO: Capitalised variables mean that developers can expect them to be publically availably +// as part of the API of the package. I don't think that is the intended case here. +type VimMode int + +const ( + NormalMode VimMode = iota + 1 + InsertMode + FindMode + LinkMode + LinkModeNewTab + LinkModeCopy + WaitMode + VisualMode + CaretMode + MakeMarkMode + GotoMarkMode +) + +// TODO: What's a mark? +type mark struct { + tabID int + URI string + xScroll int + yScroll int +} + +type HintRect struct { + Bottom int `json:"bottom"` + Top int `json:"top"` + Left int `json:"left"` + Right int `json:"right"` + Width int `json:"width"` + Height int `json:"height"` + Href string `json:"href"` +} + +var ( + // vimMode + vimMode = NormalMode + vimCommandsBindings = make(map[string]string) + keyEvents = make([]*tcell.EventKey, 11) + //runeTime = make(map[rune]time.Time) + //lastRune rune + waitModeStartTime time.Time + findText string + // marks + globalMarkMap = make(map[rune]*mark) + localMarkMap = make(map[int]map[rune]*mark) + // position coordinate for caret mode + caretPos Coordinate + // for link modes + linkText string + linkHintRects []HintRect + linkHintKeys = "asdfwerxcv" + linkHints []string + linkHintsToRects = make(map[string]*HintRect) + linkModeWithHints = true + linkHintWriteStringCalls *func() +) + +func init() { + setupLinkHints() +} + +func setupLinkHints() { + lowerAlpha := "abcdefghijklmnopqrstuvwxyz" + missingAlpha := lowerAlpha + + // Use linkHintKeys first to generate link hints + for i := 0; i < len(linkHintKeys); i++ { + for j := 0; j < len(linkHintKeys); j++ { + linkHints = append(linkHints, string(linkHintKeys[i])+string(linkHintKeys[j])) + } + missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) + } + + // missingAlpha contains all keys that aren't in linkHintKeys + // we use this to generate the last link hint key combinations, + // so this will only be used when we run out of linkHintKeys based + // link hint key combinations. + for i := 0; i < len(missingAlpha); i++ { + for j := 0; j < len(lowerAlpha); j++ { + linkHints = append(linkHints, string(missingAlpha[i])+string(lowerAlpha[j])) + } + } +} + +// Moves the caret in CaretMode. +// isCaretAtBoundary is a function that tests for the reaching of the boundaries of the given axis. +// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. +// The step size and direction is given by the value of step. +func moveVimCaret(isCaretAtBoundary func() bool, valRef *int, step int) { + var prevCell, nextCell, nextNextCell cell + var r rune + hasNextNextCell := false + + for isCaretAtBoundary() { + prevCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef += step + nextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + + if isCaretAtBoundary() { + *valRef += step + nextNextCell = getCell(caretPos.X, caretPos.Y-uiHeight) + *valRef -= step + hasNextNextCell = true + } else { + hasNextNextCell = false + } + + r = nextCell.character[0] + // Check if the next cell is different in any way + if !reflect.DeepEqual(prevCell, nextCell) { + if hasNextNextCell { + // This condition should apply to the spaces between words and the like + // Checking with unicode.isSpace() didn't give correct results for some reason + // TODO: find out what that reason is and improve this + if !unicode.IsLetter(r) && unicode.IsLetter(nextNextCell.character[0]) { + continue + } + // If the upcoming cell is deeply equal we can continue to go forward + if reflect.DeepEqual(nextCell, nextNextCell) { + continue + } + } + // This cell is different and other conditions for continuing don't apply + // therefore we stop going forward. + break + } + } +} + +// TODO: This fails if the tab with mark.tabID doesn't exist anymore it should recreate said tab, then go to the mark's URL and position +func gotoMark(mark *mark) { + if CurrentTab.ID != mark.tabID { + ensureTabExists(mark.tabID) + switchToTab(mark.tabID) + } + if CurrentTab.URI != mark.URI { + sendMessageToWebExtension("/tab_command,/url," + mark.URI) + //sleep? + } + doScrollAbsolute(mark.xScroll, mark.yScroll) +} + +// Make a mark at the current position in the current tab +func makeMark() *mark { + return &mark{CurrentTab.ID, CurrentTab.URI, CurrentTab.frame.xScroll, CurrentTab.frame.yScroll} +} + +func goIntoWaitMode() { + vimMode = WaitMode + waitModeStartTime = time.Now() +} + +func updateLinkHintDisplay() { + linkHintsToRects = make(map[string]*HintRect) + lh := len(linkHintRects) + var ht string + // List of closures + var fc []*func() + + for i, r := range linkHintRects { + // When the number of link hints is small enough + // using just one key for individual link hints suffices. + // Otherwise use the prepared link hint key combinations. + if lh <= len(linkHintKeys) { + ht = string(linkHintKeys[i]) + } else { + ht = linkHints[i] + } + // Add the key combination ht to the linkHintsToRects map. + // When the user presses it, we can easily lookup the + // link hint properties associated with it. + linkHintsToRects[ht] = &linkHintRects[i] + + // When the first key got hit, + // shorten the link hints accordingly + offsetLeft := 0 + if strings.HasPrefix(ht, linkText) { + ht = ht[len(linkText):len(ht)] + offsetLeft = len(linkText) + } + + // Make copies of parameter values + rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht + + // Link hints are in upper case in new tab mode + if vimMode == LinkModeNewTab { + htCopy = strings.ToUpper(htCopy) + } + + // Create closure + f := func() { + writeString(rLeftCopy+offsetLeft, rTopCopy+uiHeight, htCopy, tcell.StyleDefault) + } + fc = append(fc, &f) + } + // Create closure that calls the other closures + ff := func() { + for _, f := range fc { + (*f)() + } + } + linkHintWriteStringCalls = &ff +} + +func eraseLinkHints() { + linkText = "" + linkHintWriteStringCalls = nil + linkHintsToRects = make(map[string]*HintRect) + linkHintRects = nil +} + +func isNormalModeKey(ev *tcell.EventKey) bool { + if ev.Key() == tcell.KeyESC { + return true + } + return false +} + +func handleVimControl(ev *tcell.EventKey) { + var lastRune rune + command := "" + + if len(keyEvents) > 0 && keyEvents[0] != nil { + lastRune = keyEvents[len(keyEvents)-1].Rune() + } + + keyEvents = append(keyEvents, ev) + if len(keyEvents) > 10 { + keyEvents = keyEvents[1:] + } + + keyCombination := string(lastRune) + string(ev.Rune()) + + switch vimMode { + case WaitMode: + if time.Since(waitModeStartTime) < time.Millisecond*1000 { + return + } + vimMode = NormalMode + fallthrough + case NormalMode: + command = vimCommandsBindings[keyCombination] + if len(command) == 0 { + keyCombination := string(ev.Rune()) + command = vimCommandsBindings[keyCombination] + } + case InsertMode: + if isNormalModeKey(ev) { + command = "normalMode" + } + case VisualMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + if ev.Rune() == 'c' { + command = "caretMode" + } + if ev.Rune() == 'o' { + //swap cursor position begin->end or end->begin + } + if ev.Rune() == 'y' { + //clipboard + } + } + case CaretMode: + if isNormalModeKey(ev) { + command = "normalMode" + } else { + switch ev.Key() { + case tcell.KeyEnter: + generateLeftClick(caretPos.X, caretPos.Y-uiHeight) + } + switch ev.Rune() { + case 'v': + command = "visualMode" + case 'h': + moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) + case 'l': + width, _ := screen.Size() + moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) + case 'k': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) + if caretPos.Y < uiHeight { + command = "scrollHalfPageUp" + if CurrentTab.frame.yScroll == 0 { + caretPos.Y = uiHeight + } else { + caretPos.Y += (height - uiHeight) / 2 + } + } + case 'j': + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) + if caretPos.Y > height-uiHeight { + command = "scrollHalfPageDown" + caretPos.Y -= (height - uiHeight) / 2 + } + } + } + case MakeMarkMode: + if unicode.IsLower(ev.Rune()) { + if localMarkMap[CurrentTab.ID] == nil { + localMarkMap[CurrentTab.ID] = make(map[rune]*mark) + } + localMarkMap[CurrentTab.ID][ev.Rune()] = makeMark() + } else if unicode.IsUpper(ev.Rune()) { + globalMarkMap[ev.Rune()] = makeMark() + } + + command = "normalMode" + case GotoMarkMode: + if mark, ok := globalMarkMap[ev.Rune()]; ok { + gotoMark(mark) + } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { + if mark, ok := m[ev.Rune()]; ok { + gotoMark(mark) + } + } + + command = "normalMode" + case FindMode: + if isNormalModeKey(ev) { + command = "normalMode" + findText = "" + } else { + if ev.Key() == tcell.KeyEnter { + vimMode = NormalMode + command = "findText" + break + } + if ev.Key() == tcell.KeyBackspace || ev.Key() == tcell.KeyBackspace2 { + if len(findText) > 0 { + findText = findText[:len(findText)-1] + } + } else { + findText += string(ev.Rune()) + } + } + case LinkMode, LinkModeNewTab, LinkModeCopy: + if isNormalModeKey(ev) { + command = "normalMode" + eraseLinkHints() + } else { + linkText += string(ev.Rune()) + updateLinkHintDisplay() + if linkModeWithHints { + if r, ok := linkHintsToRects[linkText]; ok { + if r != nil { + switch vimMode { + case LinkMode: + if (*r).Height == 2 { + generateLeftClickYHack((*r).Left, (*r).Top, true) + } else { + generateLeftClick((*r).Left, (*r).Top) + } + case LinkModeNewTab: + //generateMiddleClick(r.Left, r.Top) + sendMessageToWebExtension("/new_tab," + r.Href) + case LinkModeCopy: + clipboard.WriteAll(r.Href) + } + goIntoWaitMode() + eraseLinkHints() + } + } + } else { + coords := findAndHighlightTextOnScreen(linkText) + if len(coords) == 1 { + goIntoWaitMode() + + if vimMode == LinkModeNewTab { + generateMiddleClick(coords[0].X, coords[0].Y) + } else { + generateLeftClick(coords[0].X, coords[0].Y) + } + linkText = "" + return + } else if len(coords) == 0 { + vimMode = NormalMode + linkText = "" + return + } + } + } + } + + if len(command) > 0 { + executeVimCommand(command) + } +} + +func executeVimCommand(command string) { + switch command { + case "urlUp": + sendMessageToWebExtension("/tab_command,/url_up") + case "urlRoot": + sendMessageToWebExtension("/tab_command,/url_root") + case "scrollToTop": + doScroll(0, -CurrentTab.frame.domRowCount()) + case "scrollToBottom": + doScroll(0, CurrentTab.frame.domRowCount()) + case "scrollUp": + doScroll(0, -1) + case "scrollDown": + doScroll(0, 1) + case "scrollLeft": + doScroll(-1, 0) + case "scrollRight": + doScroll(1, 0) + case "editURL": + urlBarFocusToggle() + case "firstTab": + switchToTab(tabsOrder[0]) + case "lastTab": + switchToTab(tabsOrder[len(tabsOrder)-1]) + case "scrollHalfPageDown": + _, height := screen.Size() + doScroll(0, (height-uiHeight)/2) + case "scrollHalfPageUp": + _, height := screen.Size() + doScroll(0, -((height - uiHeight) / 2)) + case "historyBack": + sendMessageToWebExtension("/tab_command,/history_back") + case "historyForward": + sendMessageToWebExtension("/tab_command,/history_forward") + case "reload": + sendMessageToWebExtension("/tab_command,/reload") + case "prevTab": + prevTab() + case "nextTab": + nextTab() + case "previouslyVisitedTab": + previouslyVisitedTab() + case "newTab": + createNewEmptyTab() + case "removeTab": + removeTab(CurrentTab.ID) + case "restoreTab": + restoreTab() + case "duplicateTab": + duplicateTab(CurrentTab.ID) + case "moveTabLeft": + moveTabLeft(CurrentTab.ID) + case "moveTabRight": + moveTabRight(CurrentTab.ID) + case "copyURL": + clipboard.WriteAll(CurrentTab.URI) + case "openClipboardURL": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/tab_command,/url," + URI) + case "openClipboardURLInNewTab": + URI, _ := clipboard.ReadAll() + sendMessageToWebExtension("/new_tab," + URI) + case "focusFirstTextInput": + sendMessageToWebExtension("/tab_command,/focus_first_text_input") + case "followLinkLabeledNext": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_next") + case "followLinkLabeledPrevious": + sendMessageToWebExtension("/tab_command,/follow_link_labeled_previous") + case "viewHelp": + sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") + case "openLinkInCurrentTab": + vimMode = LinkMode + sendMessageToWebExtension("/tab_command,/get_clickable_hints") + eraseLinkHints() + case "openLinkInNewTab": + vimMode = LinkModeNewTab + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "copyLinkURL": + vimMode = LinkModeCopy + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "findText": + fallthrough + case "findNext": + sendMessageToWebExtension("/tab_command,/find_next," + findText) + case "findPrevious": + sendMessageToWebExtension("/tab_command,/find_previous," + findText) + case "makeMark": + vimMode = MakeMarkMode + case "gotoMark": + vimMode = GotoMarkMode + case "insertMode": + vimMode = InsertMode + case "findMode": + vimMode = FindMode + case "normalMode": + vimMode = NormalMode + case "visualMode": + vimMode = VisualMode + case "caretMode": + vimMode = CaretMode + width, height := screen.Size() + caretPos.X, caretPos.Y = width/2, height/2 + } +} + +func searchScreenForText(text string) []Coordinate { + var offsets = make([]Coordinate, 0) + var splitString []string + var r rune + var s string + width, height := screen.Size() + screenText := "" + index := 0 + + for y := 0; y < height-uiHeight; y++ { + screenText = "" + for x := 0; x < width; x++ { + r = getCell(x, y).character[0] + s = string(r) + if len(s) == 0 || len(s) > 1 { + screenText += " " + } else { + screenText += string(getCell(x, y).character[0]) + } + } + index = 0 + splitString = strings.Split(strings.ToLower(screenText), strings.ToLower(text)) + for _, s := range splitString { + if index+len(s) >= width { + break + } + + offsets = append(offsets, Coordinate{index + len(s), y}) + index += len(s) + len(text) + } + } + return offsets +} + +func findAndHighlightTextOnScreen(text string) []Coordinate { + var x, y int + var styling = tcell.StyleDefault + + offsets := searchScreenForText(text) + for _, offset := range offsets { + y = offset.Y + x = offset.X + for z := 0; z < len(text); z++ { + screen.SetContent(x+z, y+uiHeight, rune(text[z]), nil, styling) + } + } + screen.Show() + return offsets +} + +// Parse incoming link hints +func parseJSONLinkHints(jsonString string) { + jsonBytes := []byte(jsonString) + if err := json.Unmarshal(jsonBytes, &linkHintRects); err != nil { + Shutdown(err) + } + + // Optimize link hint positions + for i := 0; i < len(linkHintRects); i++ { + r := &linkHintRects[i] + + // For links that are more than one line high + // we want to position the link hint in the vertical middle + if r.Height > 2 { + if r.Height%2 == 0 { + r.Top += r.Height / 2 + } else { + r.Top += r.Height/2 - 1 + } + } + + // For links that are more one character long we try to move + // the link hint two characters to the right, if possible. + if r.Width > 1 { + o := r.Left + r.Left += r.Width/2 - 1 + if r.Left > o+2 { + r.Left = o + 2 + } + } + } + + Log("Received parseJSONLinkHint") + // This is where the display of actual link hints is prepared + updateLinkHintDisplay() +} From eae72e94a635958876e4c115978fc6b39caf2609 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Fri, 9 Nov 2018 17:17:35 +0900 Subject: [PATCH 67/77] Adds some Vim-specific integration tests --- interfacer/src/browsh/frame_builder.go | 2 +- interfacer/src/browsh/input_box.go | 2 +- interfacer/src/browsh/tab.go | 2 +- interfacer/src/browsh/ui.go | 6 +-- interfacer/test/tty/setup.go | 3 +- interfacer/test/tty/tty_test.go | 2 +- interfacer/test/tty/vim_test.go | 51 ++++++++++++++++++++++++++ 7 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 interfacer/test/tty/vim_test.go diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index 37f90eed..e85e8693 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -312,7 +312,7 @@ func (f *frame) maybeFocusInputBox(x, y int) { left := inputBox.X right := inputBox.X + inputBox.Width if x >= left && x < right && y >= top && y < bottom { - urlBarFocus(false) + UrlBarFocus(false) inputBox.isActive = true activeInputBox = inputBox } diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index 405096e4..431297c8 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -181,7 +181,7 @@ func (i *inputBox) handleEnterKey(modifier tcell.ModMask) { } else { sendMessageToWebExtension("/url_bar," + string(i.text)) } - urlBarFocus(false) + UrlBarFocus(false) } if i.isMultiLine() && modifier != tcell.ModAlt { i.cursorInsertRune([]rune("\n")[0]) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index 900b111b..d59865b2 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -141,7 +141,7 @@ func createNewEmptyTab() { CurrentTab = tab CurrentTab.frame.resetCells() renderUI() - urlBarFocus(true) + UrlBarFocus(true) renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 6e38a1ec..e47d9c82 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -87,13 +87,13 @@ func renderURLBar() { func urlBarFocusToggle() { if urlInputBox.isActive { - urlBarFocus(false) + UrlBarFocus(false) } else { - urlBarFocus(true) + UrlBarFocus(true) } } -func urlBarFocus(on bool) { +func UrlBarFocus(on bool) { if !on { activeInputBox = nil urlInputBox.isActive = false diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index b5b0b010..876c4f12 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -15,6 +15,7 @@ import ( gomega "github.com/onsi/gomega" "browsh/interfacer/src/browsh" + "github.com/spf13/viper" ) @@ -144,7 +145,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { - SpecialKey(tcell.KeyCtrlL) + browsh.UrlBarFocus(true) Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index 8c620c40..417b860c 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -8,7 +8,7 @@ import ( . "github.com/onsi/gomega" ) -func TestIntegration(t *testing.T) { +func TestMain(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Integration tests") } diff --git a/interfacer/test/tty/vim_test.go b/interfacer/test/tty/vim_test.go new file mode 100644 index 00000000..3487a023 --- /dev/null +++ b/interfacer/test/tty/vim_test.go @@ -0,0 +1,51 @@ +package test + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestVim(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Integration tests") +} + +var _ = Describe("Vim tests", func() { + BeforeEach(func() { + GotoURL(testSiteURL + "/smorgasbord/") + }) + + It("should navigate to a new page by using a link hint", func() { + Expect("Another▄page").To(BeInFrameAt(12, 18)) + Keyboard("f") + Keyboard("a") + Expect("Another").To(BeInFrameAt(0, 0)) + }) + + It("should scroll the page by one line", func() { + Expect("[ˈsmœrɡɔsˌbuːɖ])▄is▄a").To(BeInFrameAt(12, 11)) + Keyboard("j") + Expect("type▄of▄Scandinavian▄").To(BeInFrameAt(12, 11)) + }) + + Describe("Tabs", func() { + BeforeEach(func() { + ensureOnlyOneTab() + }) + + It("should create a new tab", func() { + Keyboard("t") + Expect("New Tab").To(BeInFrameAt(21, 0)) + }) + + It("should cycle to the next tab", func() { + Keyboard("t") + GotoURL(testSiteURL + "/smorgasbord/another.html") + Keyboard("gt") + URL := testSiteURL + "/smorgasbord/ " + Expect(URL).To(BeInFrameAt(0, 1)) + }) + }) +}) From 59d2c31acf8f96b6b18555cd86eefc2bb7bab37b Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Sun, 11 Nov 2018 23:15:35 +0900 Subject: [PATCH 68/77] Fixes tests for Vim mode Vim mode still needs a lot more tests --- interfacer/src/browsh/vim_mode.go | 4 ++++ interfacer/test/tty/setup.go | 5 ++++- interfacer/test/tty/tty_test.go | 33 ++++++++++++++----------------- interfacer/test/tty/vim_test.go | 3 ++- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index cd3ad673..0ec04825 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -11,6 +11,9 @@ import ( "github.com/gdamore/tcell" ) +// TODO: A little description as to the respective responsibilties of this code versus the +// vimium.js code. + // TODO: Capitalised variables mean that developers can expect them to be publically availably // as part of the API of the package. I don't think that is the intended case here. type VimMode int @@ -71,6 +74,7 @@ var ( linkHintWriteStringCalls *func() ) +// TODO: What's this for? func init() { setupLinkHints() } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 876c4f12..2b7dbbbe 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -149,6 +149,9 @@ func GotoURL(url string) { Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() + // Hack to force text to be rerendered. Because there's a bug where text sometimes doesn't get + // rendered. + mouseClick(3, 3) // TODO: Looking for the URL isn't optimal because it could be the same URL // as the previous test. gomega.Expect(url).To(BeInFrameAt(0, 1)) @@ -214,7 +217,7 @@ func GetBgColour(x, y int) [3]int32 { } func ensureOnlyOneTab() { - if len(browsh.Tabs) > 1 { + for len(browsh.Tabs) > 1 { SpecialKey(tcell.KeyCtrlW) } } diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index 417b860c..b1380472 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -1,6 +1,7 @@ package test import ( + "browsh/interfacer/src/browsh" "testing" "github.com/gdamore/tcell" @@ -13,7 +14,7 @@ func TestMain(t *testing.T) { RunSpecs(t, "Integration tests") } -var _ = Describe("Showing a basic webpage", func() { +var _ = Describe("Core functionality", func() { BeforeEach(func() { GotoURL(testSiteURL + "/smorgasbord/") }) @@ -106,10 +107,10 @@ var _ = Describe("Showing a basic webpage", func() { It("should enter multiple lines of text", func() { Keyboard(`So here is a lot of text that will hopefully split across lines`) - Expect("So here is a lot of").To(BeInFrameAt(1, 3)) - Expect("text that will").To(BeInFrameAt(1, 4)) - Expect("hopefully split across").To(BeInFrameAt(1, 5)) - Expect("lines").To(BeInFrameAt(1, 6)) + Expect("So here is a lot of").To(BeInFrameAt(1, 2)) + Expect("text that will").To(BeInFrameAt(1, 3)) + Expect("hopefully split across").To(BeInFrameAt(1, 4)) + Expect("lines").To(BeInFrameAt(1, 5)) }) It("should scroll multiple lines of text", func() { @@ -120,37 +121,33 @@ var _ = Describe("Showing a basic webpage", func() { for i := 1; i <= 6; i++ { SpecialKey(tcell.KeyUp) } - Expect("lines").To(BeInFrameAt(1, 6)) + Expect("lines").To(BeInFrameAt(1, 5)) }) }) }) Describe("Tabs", func() { BeforeEach(func() { - SpecialKey(tcell.KeyCtrlT) - }) - - AfterEach(func() { ensureOnlyOneTab() }) It("should create a new tab", func() { - Expect("New Tab").To(BeInFrameAt(21, 0)) + SpecialKey(tcell.KeyCtrlT) + Expect(len(browsh.Tabs)).To(Equal(2)) }) It("should be able to goto a new URL", func() { - Keyboard(testSiteURL + "/smorgasbord/another.html") - SpecialKey(tcell.KeyEnter) - Expect("Another").To(BeInFrameAt(21, 0)) + SpecialKey(tcell.KeyCtrlT) + GotoURL(testSiteURL + "/smorgasbord/another.html") + Expect("Another▄webpage").To(BeInFrameAt(1, 3)) }) It("should cycle to the next tab", func() { - Expect(" ").To(BeInFrameAt(0, 1)) - SpecialKey(tcell.KeyCtrlL) + GotoURL(testSiteURL + "/smorgasbord/") + SpecialKey(tcell.KeyCtrlT) GotoURL(testSiteURL + "/smorgasbord/another.html") triggerUserKeyFor("tty.keys.next-tab") - URL := testSiteURL + "/smorgasbord/ " - Expect(URL).To(BeInFrameAt(0, 1)) + Expect("Smörgåsbord").To(BeInFrameAt(0, 0)) }) }) }) diff --git a/interfacer/test/tty/vim_test.go b/interfacer/test/tty/vim_test.go index 3487a023..b52f7d73 100644 --- a/interfacer/test/tty/vim_test.go +++ b/interfacer/test/tty/vim_test.go @@ -41,9 +41,10 @@ var _ = Describe("Vim tests", func() { }) It("should cycle to the next tab", func() { + GotoURL(testSiteURL + "/smorgasbord/") Keyboard("t") GotoURL(testSiteURL + "/smorgasbord/another.html") - Keyboard("gt") + Keyboard("J") URL := testSiteURL + "/smorgasbord/ " Expect(URL).To(BeInFrameAt(0, 1)) }) From 8161ea34e6333668d0a4505981175f632377aee6 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Sun, 11 Nov 2018 23:27:54 +0900 Subject: [PATCH 69/77] Vim mode: convert unexported symbols to lowercase --- interfacer/src/browsh/ui.go | 24 +++---- interfacer/src/browsh/vim_mode.go | 115 ++++++++++++++---------------- 2 files changed, 64 insertions(+), 75 deletions(-) diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index e47d9c82..a9e63049 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -110,30 +110,30 @@ func UrlBarFocus(on bool) { func overlayVimMode() { _, height := screen.Size() - switch vimMode { - case InsertMode: + switch currentVimMode { + case insertMode: writeString(0, height-1, "ins", tcell.StyleDefault) - case LinkMode: + case linkMode: writeString(0, height-1, "lnk", tcell.StyleDefault) - case LinkModeNewTab: + case linkModeNewTab: writeString(0, height-1, "LNK", tcell.StyleDefault) - case LinkModeCopy: + case linkModeCopy: writeString(0, height-1, "cp", tcell.StyleDefault) - case VisualMode: + case visualMode: writeString(0, height-1, "vis", tcell.StyleDefault) - case CaretMode: + case caretMode: writeString(0, height-1, "car", tcell.StyleDefault) writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) - case FindMode: + case findMode: writeString(0, height-1, "/"+findText, tcell.StyleDefault) - case MakeMarkMode: + case makeMarkMode: writeString(0, height-1, "mark", tcell.StyleDefault) - case GotoMarkMode: + case gotoMarkMode: writeString(0, height-1, "goto", tcell.StyleDefault) } - switch vimMode { - case LinkMode, LinkModeNewTab, LinkModeCopy: + switch currentVimMode { + case linkMode, linkModeNewTab, linkModeCopy: if !linkModeWithHints { findAndHighlightTextOnScreen(linkText) } diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 0ec04825..1a2f8630 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -14,22 +14,20 @@ import ( // TODO: A little description as to the respective responsibilties of this code versus the // vimium.js code. -// TODO: Capitalised variables mean that developers can expect them to be publically availably -// as part of the API of the package. I don't think that is the intended case here. -type VimMode int +type vimMode int const ( - NormalMode VimMode = iota + 1 - InsertMode - FindMode - LinkMode - LinkModeNewTab - LinkModeCopy - WaitMode - VisualMode - CaretMode - MakeMarkMode - GotoMarkMode + normalMode vimMode = iota + 1 + insertMode + findMode + linkMode + linkModeNewTab + linkModeCopy + waitMode + visualMode + caretMode + makeMarkMode + gotoMarkMode ) // TODO: What's a mark? @@ -40,7 +38,7 @@ type mark struct { yScroll int } -type HintRect struct { +type hintRect struct { Bottom int `json:"bottom"` Top int `json:"top"` Left int `json:"left"` @@ -51,34 +49,26 @@ type HintRect struct { } var ( - // vimMode - vimMode = NormalMode + currentVimMode = normalMode vimCommandsBindings = make(map[string]string) keyEvents = make([]*tcell.EventKey, 11) - //runeTime = make(map[rune]time.Time) - //lastRune rune - waitModeStartTime time.Time - findText string + waitModeStartTime time.Time + findText string // marks globalMarkMap = make(map[rune]*mark) localMarkMap = make(map[int]map[rune]*mark) - // position coordinate for caret mode + // Position coordinate for caret mode caretPos Coordinate - // for link modes + // For link modes linkText string - linkHintRects []HintRect + linkHintRects []hintRect linkHintKeys = "asdfwerxcv" linkHints []string - linkHintsToRects = make(map[string]*HintRect) + linkHintsToRects = make(map[string]*hintRect) linkModeWithHints = true linkHintWriteStringCalls *func() ) -// TODO: What's this for? -func init() { - setupLinkHints() -} - func setupLinkHints() { lowerAlpha := "abcdefghijklmnopqrstuvwxyz" missingAlpha := lowerAlpha @@ -166,12 +156,12 @@ func makeMark() *mark { } func goIntoWaitMode() { - vimMode = WaitMode + currentVimMode = waitMode waitModeStartTime = time.Now() } func updateLinkHintDisplay() { - linkHintsToRects = make(map[string]*HintRect) + linkHintsToRects = make(map[string]*hintRect) lh := len(linkHintRects) var ht string // List of closures @@ -203,7 +193,7 @@ func updateLinkHintDisplay() { rLeftCopy, rTopCopy, htCopy := r.Left, r.Top, ht // Link hints are in upper case in new tab mode - if vimMode == LinkModeNewTab { + if currentVimMode == linkModeNewTab { htCopy = strings.ToUpper(htCopy) } @@ -225,7 +215,7 @@ func updateLinkHintDisplay() { func eraseLinkHints() { linkText = "" linkHintWriteStringCalls = nil - linkHintsToRects = make(map[string]*HintRect) + linkHintsToRects = make(map[string]*hintRect) linkHintRects = nil } @@ -251,24 +241,24 @@ func handleVimControl(ev *tcell.EventKey) { keyCombination := string(lastRune) + string(ev.Rune()) - switch vimMode { - case WaitMode: + switch currentVimMode { + case waitMode: if time.Since(waitModeStartTime) < time.Millisecond*1000 { return } - vimMode = NormalMode + currentVimMode = normalMode fallthrough - case NormalMode: + case normalMode: command = vimCommandsBindings[keyCombination] if len(command) == 0 { keyCombination := string(ev.Rune()) command = vimCommandsBindings[keyCombination] } - case InsertMode: + case insertMode: if isNormalModeKey(ev) { command = "normalMode" } - case VisualMode: + case visualMode: if isNormalModeKey(ev) { command = "normalMode" } else { @@ -282,7 +272,7 @@ func handleVimControl(ev *tcell.EventKey) { //clipboard } } - case CaretMode: + case caretMode: if isNormalModeKey(ev) { command = "normalMode" } else { @@ -318,7 +308,7 @@ func handleVimControl(ev *tcell.EventKey) { } } } - case MakeMarkMode: + case makeMarkMode: if unicode.IsLower(ev.Rune()) { if localMarkMap[CurrentTab.ID] == nil { localMarkMap[CurrentTab.ID] = make(map[rune]*mark) @@ -329,7 +319,7 @@ func handleVimControl(ev *tcell.EventKey) { } command = "normalMode" - case GotoMarkMode: + case gotoMarkMode: if mark, ok := globalMarkMap[ev.Rune()]; ok { gotoMark(mark) } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { @@ -339,13 +329,13 @@ func handleVimControl(ev *tcell.EventKey) { } command = "normalMode" - case FindMode: + case findMode: if isNormalModeKey(ev) { command = "normalMode" findText = "" } else { if ev.Key() == tcell.KeyEnter { - vimMode = NormalMode + currentVimMode = normalMode command = "findText" break } @@ -357,7 +347,7 @@ func handleVimControl(ev *tcell.EventKey) { findText += string(ev.Rune()) } } - case LinkMode, LinkModeNewTab, LinkModeCopy: + case linkMode, linkModeNewTab, linkModeCopy: if isNormalModeKey(ev) { command = "normalMode" eraseLinkHints() @@ -367,17 +357,16 @@ func handleVimControl(ev *tcell.EventKey) { if linkModeWithHints { if r, ok := linkHintsToRects[linkText]; ok { if r != nil { - switch vimMode { - case LinkMode: + switch currentVimMode { + case linkMode: if (*r).Height == 2 { generateLeftClickYHack((*r).Left, (*r).Top, true) } else { generateLeftClick((*r).Left, (*r).Top) } - case LinkModeNewTab: - //generateMiddleClick(r.Left, r.Top) + case linkModeNewTab: sendMessageToWebExtension("/new_tab," + r.Href) - case LinkModeCopy: + case linkModeCopy: clipboard.WriteAll(r.Href) } goIntoWaitMode() @@ -389,7 +378,7 @@ func handleVimControl(ev *tcell.EventKey) { if len(coords) == 1 { goIntoWaitMode() - if vimMode == LinkModeNewTab { + if currentVimMode == linkModeNewTab { generateMiddleClick(coords[0].X, coords[0].Y) } else { generateLeftClick(coords[0].X, coords[0].Y) @@ -397,7 +386,7 @@ func handleVimControl(ev *tcell.EventKey) { linkText = "" return } else if len(coords) == 0 { - vimMode = NormalMode + currentVimMode = normalMode linkText = "" return } @@ -481,15 +470,15 @@ func executeVimCommand(command string) { case "viewHelp": sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") case "openLinkInCurrentTab": - vimMode = LinkMode + currentVimMode = linkMode sendMessageToWebExtension("/tab_command,/get_clickable_hints") eraseLinkHints() case "openLinkInNewTab": - vimMode = LinkModeNewTab + currentVimMode = linkModeNewTab sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "copyLinkURL": - vimMode = LinkModeCopy + currentVimMode = linkModeCopy sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "findText": @@ -499,19 +488,19 @@ func executeVimCommand(command string) { case "findPrevious": sendMessageToWebExtension("/tab_command,/find_previous," + findText) case "makeMark": - vimMode = MakeMarkMode + currentVimMode = makeMarkMode case "gotoMark": - vimMode = GotoMarkMode + currentVimMode = gotoMarkMode case "insertMode": - vimMode = InsertMode + currentVimMode = insertMode case "findMode": - vimMode = FindMode + currentVimMode = findMode case "normalMode": - vimMode = NormalMode + currentVimMode = normalMode case "visualMode": - vimMode = VisualMode + currentVimMode = visualMode case "caretMode": - vimMode = CaretMode + currentVimMode = caretMode width, height := screen.Size() caretPos.X, caretPos.Y = width/2, height/2 } From ed79db0510ee90031e1e7cd37f0718d9bd7aa592 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Mon, 12 Nov 2018 14:17:29 +0900 Subject: [PATCH 70/77] Vim mode: Small updates from PR review --- interfacer/src/browsh/browsh.go | 1 + interfacer/src/browsh/vim_mode.go | 14 +++++++------- webext/src/dom/commands_mixin.js | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index 29f776ba..3f600e1d 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -155,6 +155,7 @@ func TTYStart(injectedScreen tcell.Screen) { Log("Starting Browsh CLI client") go readStdin() startWebSocketServer() + setupLinkHints() } func toInt(char string) int { diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 1a2f8630..0543d5b7 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -54,7 +54,7 @@ var ( keyEvents = make([]*tcell.EventKey, 11) waitModeStartTime time.Time findText string - // marks + // Marks globalMarkMap = make(map[rune]*mark) localMarkMap = make(map[int]map[rune]*mark) // Position coordinate for caret mode @@ -81,9 +81,9 @@ func setupLinkHints() { missingAlpha = strings.Replace(missingAlpha, string(linkHintKeys[i]), "", -1) } - // missingAlpha contains all keys that aren't in linkHintKeys + // `missingAlpha` contains all keys that aren't in `linkHintKeys` // we use this to generate the last link hint key combinations, - // so this will only be used when we run out of linkHintKeys based + // so this will only be used when we run out of `linkHintKeys` based // link hint key combinations. for i := 0; i < len(missingAlpha); i++ { for j := 0; j < len(lowerAlpha); j++ { @@ -93,8 +93,8 @@ func setupLinkHints() { } // Moves the caret in CaretMode. -// isCaretAtBoundary is a function that tests for the reaching of the boundaries of the given axis. -// The axis of motion is decided by giving a reference to caretPos.X or caretPos.Y as valRef. +// `isCaretAtBoundary` is a function that tests for the reaching of the boundaries of the given axis. +// The axis of motion is decided by giving a reference to `caretPos.X` or `caretPos.Y` as `valRef`. // The step size and direction is given by the value of step. func moveVimCaret(isCaretAtBoundary func() bool, valRef *int, step int) { var prevCell, nextCell, nextNextCell cell @@ -506,7 +506,7 @@ func executeVimCommand(command string) { } } -func searchScreenForText(text string) []Coordinate { +func searchVisibleScreenForText(text string) []Coordinate { var offsets = make([]Coordinate, 0) var splitString []string var r rune @@ -544,7 +544,7 @@ func findAndHighlightTextOnScreen(text string) []Coordinate { var x, y int var styling = tcell.StyleDefault - offsets := searchScreenForText(text) + offsets := searchVisibleScreenForText(text) for _, offset := range offsets { y = offset.Y x = offset.X diff --git a/webext/src/dom/commands_mixin.js b/webext/src/dom/commands_mixin.js index 3541cac4..8fc8108b 100644 --- a/webext/src/dom/commands_mixin.js +++ b/webext/src/dom/commands_mixin.js @@ -124,6 +124,7 @@ export default MixinBase => this.followLinkLabeledPrevious(); } + // Eg; This goes from www.domain.com/topic/suptopic/ to www.domain.com/topic/ urlUp() { // this is taken from vimium's code var url = window.location.href; From 7a622b230b630560d31a044d5cefa035ce8f35fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Tue, 13 Nov 2018 07:42:52 -0500 Subject: [PATCH 71/77] Fixed bug where keyEvents variable was initialized wrongly. This led to key combinations only working after a certain number of key strokes. --- interfacer/src/browsh/vim_mode.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 0543d5b7..61edf878 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -51,7 +51,7 @@ type hintRect struct { var ( currentVimMode = normalMode vimCommandsBindings = make(map[string]string) - keyEvents = make([]*tcell.EventKey, 11) + keyEvents = make([]*tcell.EventKey, 0, 11) waitModeStartTime time.Time findText string // Marks From 714cad86156873e93899b0424017dbc90ed92e63 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Wed, 14 Nov 2018 23:15:23 +0900 Subject: [PATCH 72/77] Travis: upload logs to text host This is because Travis' logs had 2 problems. 1. it doesn't capture the entire log output 2. it doesn't show logs when there's a timeout --- .travis.yml | 4 ++-- interfacer/src/browsh/ui.go | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index aac01f5b..69c6a467 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,8 @@ script: - cd $REPO_ROOT/interfacer && go test test/tty/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 - cd $REPO_ROOT/interfacer && go test test/http-server/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 after_failure: - - cat $REPO_ROOT/interfacer/test/tty/debug.log - - cat $REPO_ROOT/interfacer/test/http-server/debug.log + - cat $REPO_ROOT/interfacer/test/tty/debug.log | curl -F 'f:1=<-' ix.io + - cat $REPO_ROOT/interfacer/test/http-server/debug.log | curl -F 'f:1=<-' ix.io after_success: - $REPO_ROOT/contrib/release_if_new_version.sh diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index a9e63049..43317e87 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -93,7 +93,9 @@ func urlBarFocusToggle() { } } -func UrlBarFocus(on bool) { +// Set the focus of the URL bar. Also used in tests to ensure the URL bar is in fact focussed as +// toggling doesn't guarantee that you will gain focus. +func URLBarFocus(on bool) { if !on { activeInputBox = nil urlInputBox.isActive = false From b780a79f2e8fd3f9a523fee7f6e04723f0930298 Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Wed, 14 Nov 2018 23:29:23 +0900 Subject: [PATCH 73/77] Gofmt: some minor capitalisation --- interfacer/src/browsh/frame_builder.go | 2 +- interfacer/src/browsh/input_box.go | 2 +- interfacer/src/browsh/tab.go | 10 +++++----- interfacer/src/browsh/ui.go | 4 ++-- interfacer/test/tty/setup.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index e85e8693..2a2b3af0 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -312,7 +312,7 @@ func (f *frame) maybeFocusInputBox(x, y int) { left := inputBox.X right := inputBox.X + inputBox.Width if x >= left && x < right && y >= top && y < bottom { - UrlBarFocus(false) + URLBarFocus(false) inputBox.isActive = true activeInputBox = inputBox } diff --git a/interfacer/src/browsh/input_box.go b/interfacer/src/browsh/input_box.go index 431297c8..878ebe77 100644 --- a/interfacer/src/browsh/input_box.go +++ b/interfacer/src/browsh/input_box.go @@ -181,7 +181,7 @@ func (i *inputBox) handleEnterKey(modifier tcell.ModMask) { } else { sendMessageToWebExtension("/url_bar," + string(i.text)) } - UrlBarFocus(false) + URLBarFocus(false) } if i.isMultiLine() && modifier != tcell.ModAlt { i.cursorInsertRune([]rune("\n")[0]) diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index d59865b2..b5ab03fd 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -98,8 +98,8 @@ func moveTabLeft(id int) { return } - for i, tabId := range tabsOrder { - if tabId == id { + for i, tabID := range tabsOrder { + if tabID == id { tabsOrder[i-1], tabsOrder[i] = tabsOrder[i], tabsOrder[i-1] break } @@ -113,8 +113,8 @@ func moveTabRight(id int) { return } - for i, tabId := range tabsOrder { - if tabId == id { + for i, tabID := range tabsOrder { + if tabID == id { tabsOrder[i+1], tabsOrder[i] = tabsOrder[i], tabsOrder[i+1] break } @@ -141,7 +141,7 @@ func createNewEmptyTab() { CurrentTab = tab CurrentTab.frame.resetCells() renderUI() - UrlBarFocus(true) + URLBarFocus(true) renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index 43317e87..ab7f531b 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -87,9 +87,9 @@ func renderURLBar() { func urlBarFocusToggle() { if urlInputBox.isActive { - UrlBarFocus(false) + URLBarFocus(false) } else { - UrlBarFocus(true) + URLBarFocus(true) } } diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index 2b7dbbbe..628f4e96 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -145,7 +145,7 @@ func sleepUntilPageLoad(maxTime time.Duration) { // GotoURL sends the browsh browser to the specified URL func GotoURL(url string) { - browsh.UrlBarFocus(true) + browsh.URLBarFocus(true) Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() From 935983725c09df938962f64e683dc6766f527a97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Thu, 15 Nov 2018 23:52:14 +0100 Subject: [PATCH 74/77] Added vim feature for editing URL in new tab --- interfacer/src/browsh/config.go | 1 + interfacer/src/browsh/tab.go | 8 +++++++- interfacer/src/browsh/vim_mode.go | 2 ++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go index 7405f1d5..0ffd9156 100644 --- a/interfacer/src/browsh/config.go +++ b/interfacer/src/browsh/config.go @@ -85,6 +85,7 @@ func setDefaults() { vimCommandsBindings["u"] = "scrollHalfPageUp" vimCommandsBindings["e"] = "editURL" vimCommandsBindings["ge"] = "editURL" + vimCommandsBindings["gE"] = "editURLInNewTab" vimCommandsBindings["H"] = "historyBack" vimCommandsBindings["L"] = "historyForward" vimCommandsBindings["J"] = "prevTab" diff --git a/interfacer/src/browsh/tab.go b/interfacer/src/browsh/tab.go index b5ab03fd..a212f234 100644 --- a/interfacer/src/browsh/tab.go +++ b/interfacer/src/browsh/tab.go @@ -130,18 +130,24 @@ func duplicateTab(id int) { // tab then we can't talk to it to tell it navigate. So we need to only create a real new // tab when we actually have a URL. func createNewEmptyTab() { + createNewEmptyTabWithURI("") +} + +func createNewEmptyTabWithURI(URI string) { if isNewEmptyTabActive() { return } newTab(-1) tab := Tabs[-1] tab.Title = "New Tab" - tab.URI = "" + tab.URI = URI tab.Active = true CurrentTab = tab CurrentTab.frame.resetCells() renderUI() URLBarFocus(true) + // Allows for typing directly at the end of URI + urlInputBox.selectionOff() renderCurrentTabWindow() } diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 61edf878..6a112191 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -419,6 +419,8 @@ func executeVimCommand(command string) { doScroll(1, 0) case "editURL": urlBarFocusToggle() + case "editURLInNewTab": + createNewEmptyTabWithURI(CurrentTab.URI) case "firstTab": switchToTab(tabsOrder[0]) case "lastTab": From b2ade392237b844f661cd0aa19883961b8a8da16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Gl=C3=A4=C3=9Fer?= Date: Fri, 23 Nov 2018 10:08:11 +0100 Subject: [PATCH 75/77] Fixed bug in key event handling between vim modes, where the same key event could get interpreted repeatedly. Also some rewrites/improvements of the code. Key mappings can now contain control characters and meta keys with a vim-like notation. There's a hard insert mode, which disables all of browsh's shortcuts and requires 4 hits on ESC to leave. There's a new multiple link opening feature analogous to vimium, that's still incomplete. --- interfacer/src/browsh/config.go | 102 +++++++----- interfacer/src/browsh/tty.go | 8 +- interfacer/src/browsh/ui.go | 10 +- interfacer/src/browsh/vim_mode.go | 256 +++++++++++++++++++----------- 4 files changed, 238 insertions(+), 138 deletions(-) diff --git a/interfacer/src/browsh/config.go b/interfacer/src/browsh/config.go index 0ffd9156..9e727559 100644 --- a/interfacer/src/browsh/config.go +++ b/interfacer/src/browsh/config.go @@ -75,50 +75,64 @@ func setDefaults() { viper.SetDefault("tty.keys.next-tab", []string{"\u001c", "28", "2"}) // Vim commands - vimCommandsBindings["gg"] = "scrollToTop" - vimCommandsBindings["G"] = "scrollToBottom" - vimCommandsBindings["j"] = "scrollDown" - vimCommandsBindings["k"] = "scrollUp" - vimCommandsBindings["h"] = "scrollLeft" - vimCommandsBindings["l"] = "scrollRight" - vimCommandsBindings["d"] = "scrollHalfPageDown" - vimCommandsBindings["u"] = "scrollHalfPageUp" - vimCommandsBindings["e"] = "editURL" - vimCommandsBindings["ge"] = "editURL" - vimCommandsBindings["gE"] = "editURLInNewTab" - vimCommandsBindings["H"] = "historyBack" - vimCommandsBindings["L"] = "historyForward" - vimCommandsBindings["J"] = "prevTab" - vimCommandsBindings["K"] = "nextTab" - vimCommandsBindings["r"] = "reload" - vimCommandsBindings["xx"] = "removeTab" - vimCommandsBindings["X"] = "restoreTab" - vimCommandsBindings["t"] = "newTab" - vimCommandsBindings["/"] = "findMode" - vimCommandsBindings["n"] = "findNext" - vimCommandsBindings["N"] = "findPrevious" - vimCommandsBindings["g0"] = "firstTab" - vimCommandsBindings["g$"] = "lastTab" - vimCommandsBindings["gu"] = "urlUp" - vimCommandsBindings["gU"] = "urlRoot" - vimCommandsBindings["<<"] = "moveTabLeft" - vimCommandsBindings[">>"] = "moveTabRight" - vimCommandsBindings["^"] = "previouslyVisitedTab" - vimCommandsBindings["m"] = "makeMark" - vimCommandsBindings["'"] = "gotoMark" - vimCommandsBindings["i"] = "insertMode" - vimCommandsBindings["yy"] = "copyURL" - vimCommandsBindings["p"] = "openClipboardURL" - vimCommandsBindings["P"] = "openClipboardURLInNewTab" - vimCommandsBindings["gi"] = "focusFirstTextInput" - vimCommandsBindings["f"] = "openLinkInCurrentTab" - vimCommandsBindings["F"] = "openLinkInNewTab" - vimCommandsBindings["yf"] = "copyLinkURL" - vimCommandsBindings["[["] = "followLinkLabeledPrevious" - vimCommandsBindings["]]"] = "followLinkLabeledNext" - vimCommandsBindings["yt"] = "duplicateTab" - vimCommandsBindings["v"] = "visualMode" - vimCommandsBindings["?"] = "viewHelp" + vimKeyMap["normal gg"] = "scrollToTop" + vimKeyMap["normal G"] = "scrollToBottom" + vimKeyMap["normal j"] = "scrollDown" + vimKeyMap["normal k"] = "scrollUp" + vimKeyMap["normal h"] = "scrollLeft" + vimKeyMap["normal l"] = "scrollRight" + vimKeyMap["normal d"] = "scrollHalfPageDown" + vimKeyMap["normal "] = "scrollHalfPageDown" + vimKeyMap["normal u"] = "scrollHalfPageUp" + vimKeyMap["normal "] = "scrollHalfPageUp" + vimKeyMap["normal e"] = "editURL" + vimKeyMap["normal ge"] = "editURL" + vimKeyMap["normal gE"] = "editURLInNewTab" + vimKeyMap["normal H"] = "historyBack" + vimKeyMap["normal L"] = "historyForward" + vimKeyMap["normal J"] = "prevTab" + vimKeyMap["normal K"] = "nextTab" + vimKeyMap["normal r"] = "reload" + vimKeyMap["normal xx"] = "removeTab" + vimKeyMap["normal X"] = "restoreTab" + vimKeyMap["normal t"] = "newTab" + vimKeyMap["normal T"] = "searchForTab" + vimKeyMap["normal /"] = "findMode" + vimKeyMap["normal n"] = "findNext" + vimKeyMap["normal N"] = "findPrevious" + vimKeyMap["normal g0"] = "firstTab" + vimKeyMap["normal g$"] = "lastTab" + vimKeyMap["normal gu"] = "urlUp" + vimKeyMap["normal gU"] = "urlRoot" + vimKeyMap["normal <<"] = "moveTabLeft" + vimKeyMap["normal >>"] = "moveTabRight" + vimKeyMap["normal ^"] = "previouslyVisitedTab" + vimKeyMap["normal m"] = "makeMark" + vimKeyMap["normal '"] = "gotoMark" + vimKeyMap["normal i"] = "insertMode" + vimKeyMap["normal I"] = "insertModeHard" + vimKeyMap["normal yy"] = "copyURL" + vimKeyMap["normal p"] = "openClipboardURL" + vimKeyMap["normal P"] = "openClipboardURLInNewTab" + vimKeyMap["normal gi"] = "focusFirstTextInput" + vimKeyMap["normal f"] = "openLinkInCurrentTab" + vimKeyMap["normal F"] = "openLinkInNewTab" + vimKeyMap["normal "] = "openMultipleLinksInNewTab" + vimKeyMap["normal yf"] = "copyLinkURL" + vimKeyMap["normal [["] = "followLinkLabeledPrevious" + vimKeyMap["normal ]]"] = "followLinkLabeledNext" + vimKeyMap["normal yt"] = "duplicateTab" + vimKeyMap["normal v"] = "visualMode" + vimKeyMap["normal ?"] = "viewHelp" + vimKeyMap["caret v"] = "visualMode" + vimKeyMap["caret h"] = "moveCaretLeft" + vimKeyMap["caret l"] = "moveCaretRight" + vimKeyMap["caret j"] = "moveCaretDown" + vimKeyMap["caret k"] = "moveCaretUp" + vimKeyMap["caret "] = "clickAtCaretPosition" + vimKeyMap["visual c"] = "caretMode" + vimKeyMap["visual o"] = "swapVisualModeCursorPosition" + vimKeyMap["visual y"] = "copyVisualModeSelection" } func loadConfig() { diff --git a/interfacer/src/browsh/tty.go b/interfacer/src/browsh/tty.go index f7663b5e..577cfb1a 100644 --- a/interfacer/src/browsh/tty.go +++ b/interfacer/src/browsh/tty.go @@ -57,7 +57,7 @@ func readStdin() { } } -func handleUserKeyPress(ev *tcell.EventKey) { +func handleShortcuts(ev *tcell.EventKey) { if CurrentTab == nil { if ev.Key() == tcell.KeyCtrlQ { quitBrowsh() @@ -88,6 +88,12 @@ func handleUserKeyPress(ev *tcell.EventKey) { if isKey("tty.keys.next-tab", ev) { nextTab() } +} + +func handleUserKeyPress(ev *tcell.EventKey) { + if currentVimMode != insertModeHard { + handleShortcuts(ev) + } if !urlInputBox.isActive { forwardKeyPress(ev) } diff --git a/interfacer/src/browsh/ui.go b/interfacer/src/browsh/ui.go index ab7f531b..62ac44b7 100644 --- a/interfacer/src/browsh/ui.go +++ b/interfacer/src/browsh/ui.go @@ -115,10 +115,14 @@ func overlayVimMode() { switch currentVimMode { case insertMode: writeString(0, height-1, "ins", tcell.StyleDefault) + case insertModeHard: + writeString(0, height-1, "INS", tcell.StyleDefault) case linkMode: writeString(0, height-1, "lnk", tcell.StyleDefault) case linkModeNewTab: writeString(0, height-1, "LNK", tcell.StyleDefault) + case linkModeMultipleNewTab: + writeString(0, height-1, "*LNK", tcell.StyleDefault) case linkModeCopy: writeString(0, height-1, "cp", tcell.StyleDefault) case visualMode: @@ -128,14 +132,14 @@ func overlayVimMode() { writeString(caretPos.X, caretPos.Y, "#", tcell.StyleDefault) case findMode: writeString(0, height-1, "/"+findText, tcell.StyleDefault) - case makeMarkMode: + case markModeMake: writeString(0, height-1, "mark", tcell.StyleDefault) - case gotoMarkMode: + case markModeGoto: writeString(0, height-1, "goto", tcell.StyleDefault) } switch currentVimMode { - case linkMode, linkModeNewTab, linkModeCopy: + case linkMode, linkModeNewTab, linkModeMultipleNewTab, linkModeCopy: if !linkModeWithHints { findAndHighlightTextOnScreen(linkText) } diff --git a/interfacer/src/browsh/vim_mode.go b/interfacer/src/browsh/vim_mode.go index 6a112191..cee6ee14 100644 --- a/interfacer/src/browsh/vim_mode.go +++ b/interfacer/src/browsh/vim_mode.go @@ -19,15 +19,17 @@ type vimMode int const ( normalMode vimMode = iota + 1 insertMode + insertModeHard findMode linkMode linkModeNewTab + linkModeMultipleNewTab linkModeCopy waitMode visualMode caretMode - makeMarkMode - gotoMarkMode + markModeMake + markModeGoto ) // TODO: What's a mark? @@ -49,11 +51,13 @@ type hintRect struct { } var ( - currentVimMode = normalMode - vimCommandsBindings = make(map[string]string) - keyEvents = make([]*tcell.EventKey, 0, 11) - waitModeStartTime time.Time - findText string + currentVimMode = normalMode + vimKeyMap = make(map[string]string) + keyEvents = make([]*tcell.EventKey, 0, 11) + waitModeStartTime time.Time + waitModeMaxMilliseconds = 1000 + findText string + latestKeyCombination string // Marks globalMarkMap = make(map[rune]*mark) localMarkMap = make(map[int]map[rune]*mark) @@ -156,7 +160,7 @@ func makeMark() *mark { } func goIntoWaitMode() { - currentVimMode = waitMode + changeVimMode(waitMode) waitModeStartTime = time.Now() } @@ -219,96 +223,111 @@ func eraseLinkHints() { linkHintRects = nil } +func resetLinkHints() { + linkText = "" + updateLinkHintDisplay() +} + func isNormalModeKey(ev *tcell.EventKey) bool { - if ev.Key() == tcell.KeyESC { + if ev != nil && ev.Key() == tcell.KeyESC { return true } return false } -func handleVimControl(ev *tcell.EventKey) { - var lastRune rune - command := "" +func keyEventToString(ev *tcell.EventKey) string { + if ev == nil { + return "" + } + + r := string(ev.Rune()) + if ev.Modifiers()&tcell.ModAlt != 0 && ev.Modifiers()&tcell.ModCtrl != 0 { + return "" + } else if ev.Modifiers()&tcell.ModAlt != 0 { + return "" + } else if ev.Modifiers()&tcell.ModCtrl != 0 { + return "" + } + + switch ev.Key() { + case tcell.KeyEnter: + return "" + } + + return r +} + - if len(keyEvents) > 0 && keyEvents[0] != nil { - lastRune = keyEvents[len(keyEvents)-1].Rune() +func getNLastKeyEvent(n int) *tcell.EventKey { + if n < 0 || keyEvents == nil { + return nil } + if len(keyEvents) > n { + return keyEvents[len(keyEvents)-n-1] + } + return nil +} + +func mapVimKeyEvents(ev *tcell.EventKey, mapMode string) string { + var lastEvent *tcell.EventKey + command := "" keyEvents = append(keyEvents, ev) if len(keyEvents) > 10 { keyEvents = keyEvents[1:] } - keyCombination := string(lastRune) + string(ev.Rune()) + lastEvent = getNLastKeyEvent(1) + + latestKeyCombination = keyEventToString(lastEvent) + keyEventToString(ev) + + command = vimKeyMap[mapMode+" "+latestKeyCombination] + if len(command) == 0 { + latestKeyCombination = keyEventToString(ev) + command = vimKeyMap[mapMode+" "+latestKeyCombination] + } + if len(command) <= 0 { + latestKeyCombination = "" + } else { + // Since len(command) must be greather than 0 here, + // a key mapping did match, therefore we reset keyEvents + keyEvents = nil + } + return command +} + +func handleVimMode(ev *tcell.EventKey, mode string) string { + if isNormalModeKey(ev) { + return "normalMode" + } else { + return mapVimKeyEvents(ev, mode) + } +} +func handleVimControl(ev *tcell.EventKey) { + var command string switch currentVimMode { case waitMode: - if time.Since(waitModeStartTime) < time.Millisecond*1000 { + if time.Since(waitModeStartTime) < time.Millisecond*time.Duration(waitModeMaxMilliseconds) { return } - currentVimMode = normalMode + changeVimMode(normalMode) fallthrough case normalMode: - command = vimCommandsBindings[keyCombination] - if len(command) == 0 { - keyCombination := string(ev.Rune()) - command = vimCommandsBindings[keyCombination] - } + command = mapVimKeyEvents(ev, "normal") case insertMode: - if isNormalModeKey(ev) { - command = "normalMode" - } - case visualMode: - if isNormalModeKey(ev) { + command = handleVimMode(ev, "insert") + case insertModeHard: + if isNormalModeKey(ev) && isNormalModeKey(getNLastKeyEvent(0)) && isNormalModeKey(getNLastKeyEvent(1)) && isNormalModeKey(getNLastKeyEvent(2)) { command = "normalMode" } else { - if ev.Rune() == 'c' { - command = "caretMode" - } - if ev.Rune() == 'o' { - //swap cursor position begin->end or end->begin - } - if ev.Rune() == 'y' { - //clipboard - } + command = mapVimKeyEvents(ev, "insertHard") } + case visualMode: + command = handleVimMode(ev, "visual") case caretMode: - if isNormalModeKey(ev) { - command = "normalMode" - } else { - switch ev.Key() { - case tcell.KeyEnter: - generateLeftClick(caretPos.X, caretPos.Y-uiHeight) - } - switch ev.Rune() { - case 'v': - command = "visualMode" - case 'h': - moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) - case 'l': - width, _ := screen.Size() - moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) - case 'k': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) - if caretPos.Y < uiHeight { - command = "scrollHalfPageUp" - if CurrentTab.frame.yScroll == 0 { - caretPos.Y = uiHeight - } else { - caretPos.Y += (height - uiHeight) / 2 - } - } - case 'j': - _, height := screen.Size() - moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) - if caretPos.Y > height-uiHeight { - command = "scrollHalfPageDown" - caretPos.Y -= (height - uiHeight) / 2 - } - } - } - case makeMarkMode: + command = handleVimMode(ev, "caret") + case markModeMake: if unicode.IsLower(ev.Rune()) { if localMarkMap[CurrentTab.ID] == nil { localMarkMap[CurrentTab.ID] = make(map[rune]*mark) @@ -319,7 +338,7 @@ func handleVimControl(ev *tcell.EventKey) { } command = "normalMode" - case gotoMarkMode: + case markModeGoto: if mark, ok := globalMarkMap[ev.Rune()]; ok { gotoMark(mark) } else if m, ok := localMarkMap[CurrentTab.ID]; unicode.IsLower(ev.Rune()) && ok { @@ -335,7 +354,7 @@ func handleVimControl(ev *tcell.EventKey) { findText = "" } else { if ev.Key() == tcell.KeyEnter { - currentVimMode = normalMode + changeVimMode(normalMode) command = "findText" break } @@ -347,7 +366,7 @@ func handleVimControl(ev *tcell.EventKey) { findText += string(ev.Rune()) } } - case linkMode, linkModeNewTab, linkModeCopy: + case linkMode, linkModeNewTab, linkModeMultipleNewTab, linkModeCopy: if isNormalModeKey(ev) { command = "normalMode" eraseLinkHints() @@ -366,6 +385,9 @@ func handleVimControl(ev *tcell.EventKey) { } case linkModeNewTab: sendMessageToWebExtension("/new_tab," + r.Href) + case linkModeMultipleNewTab: + resetLinkHints() + return case linkModeCopy: clipboard.WriteAll(r.Href) } @@ -386,7 +408,7 @@ func handleVimControl(ev *tcell.EventKey) { linkText = "" return } else if len(coords) == 0 { - currentVimMode = normalMode + changeVimMode(normalMode) linkText = "" return } @@ -394,13 +416,17 @@ func handleVimControl(ev *tcell.EventKey) { } } - if len(command) > 0 { - executeVimCommand(command) - } + executeVimCommand(command) } func executeVimCommand(command string) { - switch command { + if len(command) == 0 { + return + } + + currentCommand := command + command = "" + switch currentCommand { case "urlUp": sendMessageToWebExtension("/tab_command,/url_up") case "urlRoot": @@ -472,15 +498,19 @@ func executeVimCommand(command string) { case "viewHelp": sendMessageToWebExtension("/new_tab,https://www.brow.sh/docs/keybindings/") case "openLinkInCurrentTab": - currentVimMode = linkMode + changeVimMode(linkMode) sendMessageToWebExtension("/tab_command,/get_clickable_hints") eraseLinkHints() case "openLinkInNewTab": - currentVimMode = linkModeNewTab + changeVimMode(linkModeNewTab) + sendMessageToWebExtension("/tab_command,/get_link_hints") + eraseLinkHints() + case "openMultipleLinksInNewTab": + changeVimMode(linkModeMultipleNewTab) sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "copyLinkURL": - currentVimMode = linkModeCopy + changeVimMode(linkModeCopy) sendMessageToWebExtension("/tab_command,/get_link_hints") eraseLinkHints() case "findText": @@ -490,22 +520,68 @@ func executeVimCommand(command string) { case "findPrevious": sendMessageToWebExtension("/tab_command,/find_previous," + findText) case "makeMark": - currentVimMode = makeMarkMode + changeVimMode(markModeMake) case "gotoMark": - currentVimMode = gotoMarkMode + changeVimMode(markModeGoto) case "insertMode": - currentVimMode = insertMode + changeVimMode(insertMode) + case "insertModeHard": + changeVimMode(insertModeHard) case "findMode": - currentVimMode = findMode + changeVimMode(findMode) case "normalMode": - currentVimMode = normalMode + changeVimMode(normalMode) + // Visual mode case "visualMode": - currentVimMode = visualMode + changeVimMode(visualMode) + case "swapVisualModeCursorPosition": + // Stub + case "copyVisualModeSelection": + // Caret mode case "caretMode": - currentVimMode = caretMode + changeVimMode(caretMode) width, height := screen.Size() caretPos.X, caretPos.Y = width/2, height/2 + case "clickAtCaretPosition": + generateLeftClick(caretPos.X, caretPos.Y-uiHeight) + case "moveCaretLeft": + moveVimCaret(func() bool { return caretPos.X > 0 }, &caretPos.X, -1) + case "moveCaretRight": + width, _ := screen.Size() + moveVimCaret(func() bool { return caretPos.X < width }, &caretPos.X, 1) + case "moveCaretUp": + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y >= uiHeight }, &caretPos.Y, -1) + if caretPos.Y < uiHeight { + command = "scrollHalfPageUp" + if CurrentTab.frame.yScroll == 0 { + caretPos.Y = uiHeight + } else { + caretPos.Y += (height - uiHeight) / 2 + } + } + case "moveCaretDown": + _, height := screen.Size() + moveVimCaret(func() bool { return caretPos.Y <= height-uiHeight }, &caretPos.Y, 1) + if caretPos.Y > height-uiHeight { + command = "scrollHalfPageDown" + caretPos.Y -= (height - uiHeight) / 2 + } } + + // A command can spawn another + executeVimCommand(command) +} + +func changeVimMode(mode vimMode) { + if currentVimMode == mode { + // No change + return + } + + currentVimMode = mode + // Reset keyEvents + keyEvents = nil } func searchVisibleScreenForText(text string) []Coordinate { From 21081ad6709a71f2aa8b74080148dbb7d28657de Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 24 Jun 2019 18:52:58 -0700 Subject: [PATCH 76/77] merge vim_test into tty_test --- .travis.yml | 4 +-- interfacer/test/tty/setup.go | 5 +--- interfacer/test/tty/tty_test.go | 7 +++-- interfacer/test/tty/vim_test.go | 52 --------------------------------- 4 files changed, 7 insertions(+), 61 deletions(-) delete mode 100644 interfacer/test/tty/vim_test.go diff --git a/.travis.yml b/.travis.yml index 69c6a467..aac01f5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,8 +40,8 @@ script: - cd $REPO_ROOT/interfacer && go test test/tty/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 - cd $REPO_ROOT/interfacer && go test test/http-server/*.go -v -ginkgo.slowSpecThreshold=30 -ginkgo.flakeAttempts=3 after_failure: - - cat $REPO_ROOT/interfacer/test/tty/debug.log | curl -F 'f:1=<-' ix.io - - cat $REPO_ROOT/interfacer/test/http-server/debug.log | curl -F 'f:1=<-' ix.io + - cat $REPO_ROOT/interfacer/test/tty/debug.log + - cat $REPO_ROOT/interfacer/test/http-server/debug.log after_success: - $REPO_ROOT/contrib/release_if_new_version.sh diff --git a/interfacer/test/tty/setup.go b/interfacer/test/tty/setup.go index a89944a5..f8566595 100644 --- a/interfacer/test/tty/setup.go +++ b/interfacer/test/tty/setup.go @@ -152,9 +152,6 @@ func GotoURL(url string) { Keyboard(url) SpecialKey(tcell.KeyEnter) WaitForPageLoad() - // Hack to force text to be rerendered. Because there's a bug where text sometimes doesn't get - // rendered. - mouseClick(3, 3) // TODO: Looking for the URL isn't optimal because it could be the same URL // as the previous test. gomega.Expect(url).To(BeInFrameAt(0, 1)) @@ -230,7 +227,7 @@ func GetBgColour(x, y int) [3]int32 { } func ensureOnlyOneTab() { - for len(browsh.Tabs) > 1 { + if len(browsh.Tabs) > 1 { SpecialKey(tcell.KeyCtrlW) } } diff --git a/interfacer/test/tty/tty_test.go b/interfacer/test/tty/tty_test.go index f9d70c34..92a35175 100644 --- a/interfacer/test/tty/tty_test.go +++ b/interfacer/test/tty/tty_test.go @@ -10,12 +10,12 @@ import ( . "github.com/onsi/gomega" ) -func TestMain(t *testing.T) { +func TestIntegration(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Integration tests") } -var _ = Describe("Core functionality", func() { +var _ = Describe("Showing a basic webpage", func() { BeforeEach(func() { GotoURL(testSiteURL + "/smorgasbord/") }) @@ -180,7 +180,8 @@ var _ = Describe("Core functionality", func() { SpecialKey(tcell.KeyEnter) Expect("Another").To(BeInFrameAt(21, 0)) triggerUserKeyFor("tty.keys.next-tab") - Expect("Smörgåsbord").To(BeInFrameAt(0, 0)) + URL := testSiteURL + "/smorgasbord/ " + Expect(URL).To(BeInFrameAt(0, 1)) }) It("should create a new tab", func() { diff --git a/interfacer/test/tty/vim_test.go b/interfacer/test/tty/vim_test.go deleted file mode 100644 index b52f7d73..00000000 --- a/interfacer/test/tty/vim_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestVim(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Integration tests") -} - -var _ = Describe("Vim tests", func() { - BeforeEach(func() { - GotoURL(testSiteURL + "/smorgasbord/") - }) - - It("should navigate to a new page by using a link hint", func() { - Expect("Another▄page").To(BeInFrameAt(12, 18)) - Keyboard("f") - Keyboard("a") - Expect("Another").To(BeInFrameAt(0, 0)) - }) - - It("should scroll the page by one line", func() { - Expect("[ˈsmœrɡɔsˌbuːɖ])▄is▄a").To(BeInFrameAt(12, 11)) - Keyboard("j") - Expect("type▄of▄Scandinavian▄").To(BeInFrameAt(12, 11)) - }) - - Describe("Tabs", func() { - BeforeEach(func() { - ensureOnlyOneTab() - }) - - It("should create a new tab", func() { - Keyboard("t") - Expect("New Tab").To(BeInFrameAt(21, 0)) - }) - - It("should cycle to the next tab", func() { - GotoURL(testSiteURL + "/smorgasbord/") - Keyboard("t") - GotoURL(testSiteURL + "/smorgasbord/another.html") - Keyboard("J") - URL := testSiteURL + "/smorgasbord/ " - Expect(URL).To(BeInFrameAt(0, 1)) - }) - }) -}) From 889263ac076eda2f2f5185d195b34d1270f5ca9c Mon Sep 17 00:00:00 2001 From: Thomas Buckley-Houston Date: Mon, 25 Jul 2022 23:16:46 -0400 Subject: [PATCH 77/77] Linting --- interfacer/src/browsh/browsh.go | 2 +- interfacer/src/browsh/frame_builder.go | 4 +- interfacer/src/browsh/input_multiline.go | 4 +- webext/src/background/manager.js | 16 +-- webext/src/dom/commands_mixin.js | 4 +- webext/src/dom/manager.js | 6 +- webext/src/vimium.js | 162 +++++++++++------------ 7 files changed, 99 insertions(+), 99 deletions(-) diff --git a/interfacer/src/browsh/browsh.go b/interfacer/src/browsh/browsh.go index 5982b201..466a358d 100644 --- a/interfacer/src/browsh/browsh.go +++ b/interfacer/src/browsh/browsh.go @@ -3,7 +3,6 @@ package browsh import ( "encoding/base64" "fmt" - "time" "io/ioutil" "net/url" "os" @@ -12,6 +11,7 @@ import ( "runtime" "strconv" "strings" + "time" // TCell seems to be one of the best projects in any language for handling terminal // standards across the major OSs. diff --git a/interfacer/src/browsh/frame_builder.go b/interfacer/src/browsh/frame_builder.go index 202edc8d..001940df 100644 --- a/interfacer/src/browsh/frame_builder.go +++ b/interfacer/src/browsh/frame_builder.go @@ -90,8 +90,8 @@ func (f *frame) buildFrameText(incoming incomingFrameText) { return } - var s = "/frame_text "; - for _,c := range incoming.Text { + var s = "/frame_text " + for _, c := range incoming.Text { if c != "" { s = s + c } diff --git a/interfacer/src/browsh/input_multiline.go b/interfacer/src/browsh/input_multiline.go index dc21cf31..e9b94c46 100644 --- a/interfacer/src/browsh/input_multiline.go +++ b/interfacer/src/browsh/input_multiline.go @@ -29,8 +29,8 @@ func (m *multiLine) convert() []rune { if m.isInsideWord() { // TODO: This sometimes causes a panic :/ if m.currentCharacter != "" { - m.currentWordish += m.currentCharacter - } + m.currentWordish += m.currentCharacter + } } else { m.addWhitespace() } diff --git a/webext/src/background/manager.js b/webext/src/background/manager.js index 3e722388..d1fb5b25 100644 --- a/webext/src/background/manager.js +++ b/webext/src/background/manager.js @@ -128,7 +128,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { _handleTabUpdate(_tab_id, changes, native_tab_object) { this.log( - `Tab ${native_tab_object.id} detected chages: ${JSON.stringify(changes)}`, + `Tab ${native_tab_object.id} detected chages: ${JSON.stringify(changes)}` ); let tab = this.tabs[native_tab_object.id]; tab.native_last_change = changes; @@ -141,7 +141,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { // until it knows its internally represented ID. _newTabHandler(_request, sender, sendResponse) { this.log( - `Tab ${sender.tab.id} (${sender.tab.title}) registered with background process`, + `Tab ${sender.tab.id} (${sender.tab.title}) registered with background process` ); // Send the tab back to itself, such that it can be enlightened unto its own nature sendResponse(sender.tab); @@ -180,13 +180,13 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { // This is the main communication channel for all back and forth messages to tabs _listenForTabChannelOpen() { browser.runtime.onConnect.addListener( - this._tabChannelOpenHandler.bind(this), + this._tabChannelOpenHandler.bind(this) ); } _tabChannelOpenHandler(channel) { this.log( - `Tab ${channel.name} connected for communication with background process`, + `Tab ${channel.name} connected for communication with background process` ); let tab = this.tabs[parseInt(channel.name)]; tab.postConnectionInit(channel, this.config); @@ -224,7 +224,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { }); getting.then( (windowInfoArray) => this._getTabsOnSuccess(windowInfoArray, callback), - () => this._getTabsOnError(callback), + () => this._getTabsOnError(callback) ); } @@ -249,7 +249,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { this.log( "BACKGROUND: Frame loop starting at " + this.config.tty.small_pixel_frame_rate + - "ms intervals", + "ms intervals" ); setInterval(() => { if (this._is_initial_window_size_pending) this._initialWindowResize(); @@ -273,7 +273,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { } if (this.currentTab().channel === undefined) { this.log( - `Active tab ${this.active_tab_id} does not have a channel, so not requesting a frame`, + `Active tab ${this.active_tab_id} does not have a channel, so not requesting a frame` ); return false; } @@ -295,7 +295,7 @@ export default class extends utils.mixins(CommonMixin, TTYCommandsMixin) { { urls: ["*://*/*"], }, - ["blocking"], + ["blocking"] ); } } diff --git a/webext/src/dom/commands_mixin.js b/webext/src/dom/commands_mixin.js index 48edacca..62afe781 100644 --- a/webext/src/dom/commands_mixin.js +++ b/webext/src/dom/commands_mixin.js @@ -94,7 +94,7 @@ export default (MixinBase) => //adapted vimium code followLinkLabeledNext() { var nextPatterns = "next,more,newer,>,›,→,»,≫,>>,weiter" || ""; - var nextStrings = nextPatterns.split(",").filter(function(s) { + var nextStrings = nextPatterns.split(",").filter(function (s) { return s.trim().length; }); return ( @@ -111,7 +111,7 @@ export default (MixinBase) => followLinkLabeledPrevious() { var previousPatterns = "prev,previous,back,older,<,‹,←,«,≪,<<,zurück" || ""; - var previousStrings = previousPatterns.split(",").filter(function(s) { + var previousStrings = previousPatterns.split(",").filter(function (s) { return s.trim().length; }); return ( diff --git a/webext/src/dom/manager.js b/webext/src/dom/manager.js index b6d12d95..d4ad83c9 100644 --- a/webext/src/dom/manager.js +++ b/webext/src/dom/manager.js @@ -30,13 +30,13 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { this.graphics_builder = new GraphicsBuilder( this.channel, this.dimensions, - this.config, + this.config ); this.text_builder = new TextBuilder( this.channel, this.dimensions, this.graphics_builder, - this.config, + this.config ); } @@ -171,7 +171,7 @@ export default class extends utils.mixins(CommonMixin, CommandsMixin) { let sending = browser.runtime.sendMessage("/register"); sending.then( (r) => this._registrationSuccess(r), - (e) => this._registrationError(e), + (e) => this._registrationError(e) ); } diff --git a/webext/src/vimium.js b/webext/src/vimium.js index 246c5353..f2f4a07e 100644 --- a/webext/src/vimium.js +++ b/webext/src/vimium.js @@ -39,27 +39,27 @@ // because Browsh is currently assuming that it is being run in Firefox. var Rect = { - create: function(x1, y1, x2, y2) { + create: function (x1, y1, x2, y2) { return { bottom: y2, top: y1, left: x1, right: x2, width: x2 - x1, - height: y2 - y1 + height: y2 - y1, }; }, - copy: function(rect) { + copy: function (rect) { return { bottom: rect.bottom, top: rect.top, left: rect.left, right: rect.right, width: rect.width, - height: rect.height + height: rect.height, }; }, - translate: function(rect, x, y) { + translate: function (rect, x, y) { if (x == null) { x = 0; } @@ -72,10 +72,10 @@ var Rect = { left: rect.left + x, right: rect.right + x, width: rect.width, - height: rect.height + height: rect.height, }; }, - subtract: function(rect1, rect2) { + subtract: function (rect1, rect2) { var rects; rect2 = this.create( Math.max(rect1.left, rect2.left), @@ -94,13 +94,13 @@ var Rect = { this.create(rect2.right, rect2.top, rect1.right, rect2.bottom), this.create(rect1.left, rect2.bottom, rect2.left, rect1.bottom), this.create(rect2.left, rect2.bottom, rect2.right, rect1.bottom), - this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom) + this.create(rect2.right, rect2.bottom, rect1.right, rect1.bottom), ]; - return rects.filter(function(rect) { + return rects.filter(function (rect) { return rect.height > 0 && rect.width > 0; }); }, - intersects: function(rect1, rect2) { + intersects: function (rect1, rect2) { return ( rect1.right > rect2.left && rect1.left < rect2.right && @@ -108,7 +108,7 @@ var Rect = { rect1.top < rect2.bottom ); }, - intersectsStrict: function(rect1, rect2) { + intersectsStrict: function (rect1, rect2) { return ( rect1.right >= rect2.left && rect1.left <= rect2.right && @@ -116,7 +116,7 @@ var Rect = { rect1.top <= rect2.bottom ); }, - equals: function(rect1, rect2) { + equals: function (rect1, rect2) { var i, len, property, ref; ref = ["top", "bottom", "left", "right", "width", "height"]; for (i = 0, len = ref.length; i < len; i++) { @@ -127,18 +127,18 @@ var Rect = { } return true; }, - intersect: function(rect1, rect2) { + intersect: function (rect1, rect2) { return this.create( Math.max(rect1.left, rect2.left), Math.max(rect1.top, rect2.top), Math.min(rect1.right, rect2.right), Math.min(rect1.bottom, rect2.bottom) ); - } + }, }; var DomUtils = { - documentReady: function() { + documentReady: function () { var callbacks, isReady, onDOMContentLoaded, ref; (ref = [document.readyState !== "loading", []]), (isReady = ref[0]), @@ -146,7 +146,7 @@ var DomUtils = { if (!isReady) { window.addEventListener( "DOMContentLoaded", - (onDOMContentLoaded = forTrusted(function() { + (onDOMContentLoaded = forTrusted(function () { var callback, i, len; window.removeEventListener("DOMContentLoaded", onDOMContentLoaded); isReady = true; @@ -158,7 +158,7 @@ var DomUtils = { })) ); } - return function(callback) { + return function (callback) { if (isReady) { return callback(); } else { @@ -166,7 +166,7 @@ var DomUtils = { } }; }, - getVisibleClientRect: function(element, testChildren) { + getVisibleClientRect: function (element, testChildren) { var child, childClientRect, clientRect, @@ -182,7 +182,7 @@ var DomUtils = { if (testChildren == null) { testChildren = false; } - clientRects = (function() { + clientRects = (function () { var i, len, ref, results; ref = element.getClientRects(); results = []; @@ -192,14 +192,14 @@ var DomUtils = { } return results; })(); - isInlineZeroHeight = function() { + isInlineZeroHeight = function () { var elementComputedStyle, isInlineZeroFontSize; elementComputedStyle = window.getComputedStyle(element, null); isInlineZeroFontSize = 0 === elementComputedStyle.getPropertyValue("display").indexOf("inline") && elementComputedStyle.getPropertyValue("font-size") === "0px"; - isInlineZeroHeight = function() { + isInlineZeroHeight = function () { return isInlineZeroFontSize; }; return isInlineZeroFontSize; @@ -253,7 +253,7 @@ var DomUtils = { } return null; }, - cropRectToVisible: function(rect) { + cropRectToVisible: function (rect) { var boundedRect; boundedRect = Rect.create( Math.max(rect.left, 0), @@ -270,7 +270,7 @@ var DomUtils = { return boundedRect; } }, - getClientRectsForAreas: function(imgClientRect, areas) { + getClientRectsForAreas: function (imgClientRect, areas) { var area, coords, diff, @@ -290,7 +290,7 @@ var DomUtils = { rects = []; for (i = 0, len = areas.length; i < len; i++) { area = areas[i]; - coords = area.coords.split(",").map(function(coord) { + coords = area.coords.split(",").map(function (coord) { return parseInt(coord, 10); }); shape = area.shape.toLowerCase(); @@ -321,13 +321,13 @@ var DomUtils = { if (rect && !isNaN(rect.top)) { rects.push({ element: area, - rect: rect + rect: rect, }); } } return rects; }, - isSelectable: function(element) { + isSelectable: function (element) { var unselectableTypes; if (!(element instanceof Element)) { return false; @@ -341,7 +341,7 @@ var DomUtils = { "image", "radio", "reset", - "submit" + "submit", ]; return ( (element.nodeName.toLowerCase() === "input" && @@ -350,7 +350,7 @@ var DomUtils = { element.isContentEditable ); }, - getViewportTopLeft: function() { + getViewportTopLeft: function () { var box, clientLeft, clientTop, marginLeft, marginTop, rect, style; box = document.documentElement; style = getComputedStyle(box); @@ -363,7 +363,7 @@ var DomUtils = { marginLeft = parseInt(style.marginLeft); return { top: -rect.top + marginTop, - left: -rect.left + marginLeft + left: -rect.left + marginLeft, }; } else { //if (Utils.isFirefox()) @@ -375,11 +375,11 @@ var DomUtils = { } return { top: -rect.top - clientTop, - left: -rect.left - clientLeft + left: -rect.left - clientLeft, }; } }, - makeXPath: function(elementArray) { + makeXPath: function (elementArray) { var element, i, len, xpath; xpath = []; for (i = 0, len = elementArray.length; i < len; i++) { @@ -388,12 +388,12 @@ var DomUtils = { } return xpath.join(" | "); }, - evaluateXPath: function(xpath, resultType) { + evaluateXPath: function (xpath, resultType) { var contextNode, namespaceResolver; contextNode = document.webkitIsFullScreen ? document.webkitFullscreenElement : document.documentElement; - namespaceResolver = function(namespace) { + namespaceResolver = function (namespace) { if (namespace === "xhtml") { return "http://www.w3.org/1999/xhtml"; } else { @@ -408,7 +408,7 @@ var DomUtils = { null ); }, - simulateClick: function(element, modifiers) { + simulateClick: function (element, modifiers) { var defaultActionShouldTrigger, event, eventSequence, i, len, results; if (modifiers == null) { modifiers = {}; @@ -438,10 +438,10 @@ var DomUtils = { } return results; }, - simulateMouseEvent: (function() { + simulateMouseEvent: (function () { var lastHoveredElement; lastHoveredElement = void 0; - return function(event, element, modifiers) { + return function (event, element, modifiers) { var mouseEvent; if (modifiers == null) { modifiers = {}; @@ -479,7 +479,7 @@ var DomUtils = { return element.dispatchEvent(mouseEvent); }; })(), - simulateClickDefaultAction: function(element, modifiers) { + simulateClickDefaultAction: function (element, modifiers) { var altKey, ctrlKey, metaKey, newTabModifier, ref, shiftKey; if (modifiers == null) { modifiers = {}; @@ -505,7 +505,7 @@ var DomUtils = { chrome.runtime.sendMessage({ handler: "openUrlInNewTab", url: element.href, - active: shiftKey === true + active: shiftKey === true, }); } else if ( shiftKey === true && @@ -515,20 +515,20 @@ var DomUtils = { ) { chrome.runtime.sendMessage({ handler: "openUrlInNewWindow", - url: element.href + url: element.href, }); } else if (element.target === "_blank") { chrome.runtime.sendMessage({ handler: "openUrlInNewTab", url: element.href, - active: true + active: true, }); } - } + }, }; var LocalHints = { - getVisibleClickable: function(element) { + getVisibleClickable: function (element) { var actionName, areas, areasAndRects, @@ -603,16 +603,16 @@ var LocalHints = { ? ref2.toLowerCase() : void 0) === "" || ref1 === "true" || - ((ref3 = + (ref3 = (ref4 = element.getAttribute("aria-disabled")) != null ? ref4.toLowerCase() : void 0) === "" || - ref3 === "true") + ref3 === "true" ) { return []; } if (this.checkForAngularJs == null) { - this.checkForAngularJs = (function() { + this.checkForAngularJs = (function () { var angularElements, i, k, @@ -625,7 +625,7 @@ var LocalHints = { separator; angularElements = document.getElementsByClassName("ng-scope"); if (angularElements.length === 0) { - return function() { + return function () { return false; }; } else { @@ -639,7 +639,7 @@ var LocalHints = { ngAttributes.push(prefix + "ng" + separator + "click"); } } - return function(element) { + return function (element) { var attribute, l, len2; for (l = 0, len2 = ngAttributes.length; l < len2; l++) { attribute = ngAttributes[l]; @@ -679,7 +679,7 @@ var LocalHints = { (ref8 = ruleSplit.length === 1 ? ["click"].concat(slice.call(ruleSplit[0].trim().split(".")), [ - "_" + "_", ]) : [ruleSplit[0]].concat( slice.call(ruleSplit[1].trim().split(".")), @@ -775,13 +775,13 @@ var LocalHints = { rect: clientRect, secondClassCitizen: onlyHasTabIndex, possibleFalsePositive: possibleFalsePositive, - reason: reason + reason: reason, }); } } return visibleElements; }, - getLocalHints: function(requireHref) { + getLocalHints: function (requireHref) { var descendantsToCheck, element, elements, @@ -822,7 +822,7 @@ var LocalHints = { } visibleElements = visibleElements.reverse(); descendantsToCheck = [1, 2, 3]; - visibleElements = (function() { + visibleElements = (function () { var k, len1, results; results = []; for ( @@ -833,7 +833,7 @@ var LocalHints = { element = visibleElements[position]; if ( element.possibleFalsePositive && - (function() { + (function () { var _, candidateDescendant, index, l, len2; index = Math.max(0, position - 6); while (index < position) { @@ -866,7 +866,7 @@ var LocalHints = { negativeRect = visibleElements[k].rect; rects = (ref = []).concat.apply( ref, - rects.map(function(rect) { + rects.map(function (rect) { return Rect.subtract(rect, negativeRect); }) ); @@ -903,7 +903,7 @@ var LocalHints = { }*/ return localHints; }, - generateLinkText: function(hint) { + generateLinkText: function (hint) { var element, linkText, nodeName, ref, showLinkText; element = hint.element; linkText = ""; @@ -951,13 +951,13 @@ var LocalHints = { } return { linkText: linkText.trim(), - showLinkText: showLinkText + showLinkText: showLinkText, }; - } + }, }; var VimiumNormal = { - followLink: function(linkElement) { + followLink: function (linkElement) { if (linkElement.nodeName.toLowerCase() === "link") { return (window.location.href = linkElement.href); } else { @@ -965,7 +965,7 @@ var VimiumNormal = { return DomUtils.simulateClick(linkElement); } }, - findAndFollowLink: function(linkStrings) { + findAndFollowLink: function (linkStrings) { var boundingClientRect, candidateLink, candidateLinks, @@ -990,7 +990,7 @@ var VimiumNormal = { ref1; linksXPath = DomUtils.makeXPath([ "a", - "*[@onclick or @role='link' or contains(@class, 'button')]" + "*[@onclick or @role='link' or contains(@class, 'button')]", ]); links = DomUtils.evaluateXPath( linksXPath, @@ -1038,18 +1038,18 @@ var VimiumNormal = { link = candidateLinks[l]; link.wordCount = link.innerText.trim().split(/\s+/).length; } - candidateLinks.forEach(function(a, i) { + candidateLinks.forEach(function (a, i) { return (a.originalIndex = i); }); candidateLinks = candidateLinks - .sort(function(a, b) { + .sort(function (a, b) { if (a.wordCount === b.wordCount) { return a.originalIndex - b.originalIndex; } else { return a.wordCount - b.wordCount; } }) - .filter(function(a) { + .filter(function (a) { return a.wordCount <= candidateLinks[0].wordCount + 1; }); for (m = 0, len2 = linkStrings.length; m < len2; m++) { @@ -1071,7 +1071,7 @@ var VimiumNormal = { } return false; }, - findAndFollowRel: function(value) { + findAndFollowRel: function (value) { var element, elements, j, k, len, len1, relTags, tag; relTags = ["link", "a", "area"]; for (j = 0, len = relTags.length; j < len; j++) { @@ -1089,7 +1089,7 @@ var VimiumNormal = { } } }, - textInputXPath: function() { + textInputXPath: function () { var inputElements, textInputTypes; textInputTypes = [ "text", @@ -1099,26 +1099,26 @@ var VimiumNormal = { "number", "password", "date", - "tel" + "tel", ]; inputElements = [ "input[" + "(" + textInputTypes - .map(function(type) { + .map(function (type) { return '@type="' + type + '"'; }) .join(" or ") + "or not(@type))" + " and not(@disabled or @readonly)]", "textarea", - "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']" + "*[@contenteditable='' or translate(@contenteditable, 'TRUE', 'true')='true']", ]; return typeof DomUtils !== "undefined" && DomUtils !== null ? DomUtils.makeXPath(inputElements) : void 0; }, - focusInput: function(count) { + focusInput: function (count) { var element, elements, hint, @@ -1133,7 +1133,7 @@ var VimiumNormal = { textInputXPath, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE ); - visibleInputs = (function() { + visibleInputs = (function () { var j, ref, results; results = []; for (i = j = 0, ref = resultSet.snapshotLength; j < ref; i = j += 1) { @@ -1144,12 +1144,12 @@ var VimiumNormal = { results.push({ element: element, index: i, - rect: Rect.copy(element.getBoundingClientRect()) + rect: Rect.copy(element.getBoundingClientRect()), }); } return results; })(); - visibleInputs.sort(function(arg, arg1) { + visibleInputs.sort(function (arg, arg1) { var element1, element2, i1, i2, tabDifference; (element1 = arg.element), (i1 = arg.index); (element2 = arg1.element), (i2 = arg1.index); @@ -1177,12 +1177,12 @@ var VimiumNormal = { recentlyFocusedElement = lastFocusedInput(); selectedInputIndex = count === 1 - ? ((elements = visibleInputs.map(function(visibleInput) { + ? ((elements = visibleInputs.map(function (visibleInput) { return visibleInput.element; })), Math.max(0, elements.indexOf(recentlyFocusedElement))) : Math.min(count, visibleInputs.length) - 1; - hints = (function() { + hints = (function () { var j, len, results; results = []; for (j = 0, len = visibleInputs.length; j < len; j++) { @@ -1198,13 +1198,13 @@ var VimiumNormal = { return results; })(); return new FocusSelector(hints, visibleInputs, selectedInputIndex); - } + }, }; export function MiscVimium() { if (window.forTrusted == null) { - window.forTrusted = function(handler) { - return function(event) { + window.forTrusted = function (handler) { + return function (event) { if (event != null ? event.isTrusted : void 0) { return handler.apply(this, arguments); } else { @@ -1213,15 +1213,15 @@ export function MiscVimium() { }; }; } - window.windowIsFocused = function() { + window.windowIsFocused = function () { var windowHasFocus; windowHasFocus = null; - DomUtils.documentReady(function() { + DomUtils.documentReady(function () { return (windowHasFocus = document.hasFocus()); }); window.addEventListener( "focus", - forTrusted(function(event) { + forTrusted(function (event) { if (event.target === window) { windowHasFocus = true; } @@ -1230,14 +1230,14 @@ export function MiscVimium() { ); window.addEventListener( "blur", - forTrusted(function(event) { + forTrusted(function (event) { if (event.target === window) { windowHasFocus = false; } return true; }) ); - return function() { + return function () { return windowHasFocus; }; };