visbug-lib
Version:
<p align="center"> <img src="./assets/visbug.png" width="300" height="300" alt="visbug"> <br> <a href="https://www.npmjs.org/package/visbug"><img src="https://img.shields.io/npm/v/visbug.svg?style=flat" alt="npm latest version number"></a> <a href
832 lines (698 loc) • 783 kB
JavaScript
const sugar = {
on: function(names, fn) {
names
.split(' ')
.forEach(name =>
this.addEventListener(name, fn));
return this
},
off: function(names, fn) {
names
.split(' ')
.forEach(name =>
this.removeEventListener(name, fn));
return this
},
attr: function(attr, val) {
if (val === undefined) return this.getAttribute(attr)
val == null
? this.removeAttribute(attr)
: this.setAttribute(attr, val || '');
return this
}
};
function $(query, $context = document) {
let $nodes = query instanceof NodeList || Array.isArray(query)
? query
: query instanceof HTMLElement || query instanceof SVGElement
? [query]
: $context.querySelectorAll(query);
if (!$nodes.length) $nodes = [];
return Object.assign(
[...$nodes].map($el => Object.assign($el, sugar)),
{
on: function(names, fn) {
this.forEach($el => $el.on(names, fn));
return this
},
off: function(names, fn) {
this.forEach($el => $el.off(names, fn));
return this
},
attr: function(attrs, val) {
if (typeof attrs === 'string' && val === undefined)
return this[0].attr(attrs)
else if (typeof attrs === 'object')
this.forEach($el =>
Object.entries(attrs)
.forEach(([key, val]) =>
$el.attr(key, val)));
else if (typeof attrs == 'string' && (val || val == null || val == ''))
this.forEach($el => $el.attr(attrs, val));
return this
}
}
)
}
/*!
* hotkeys-js v3.6.2
* A simple micro-library for defining and dispatching keyboard shortcuts. It has no dependencies.
*
* Copyright (c) 2019 kenny wong <wowohoo@qq.com>
* http://jaywcjlove.github.io/hotkeys
*
* Licensed under the MIT license.
*/
var isff = typeof navigator !== 'undefined' ? navigator.userAgent.toLowerCase().indexOf('firefox') > 0 : false;
// 绑定事件
function addEvent(object, event, method) {
if (object.addEventListener) {
object.addEventListener(event, method, false);
} else if (object.attachEvent) {
object.attachEvent('on' + event, function () {
method(window.event);
});
}
}
// 修饰键转换成对应的键码
function getMods(modifier, key) {
var mods = key.slice(0, key.length - 1);
for (var i = 0; i < mods.length; i++) {
mods[i] = modifier[mods[i].toLowerCase()];
}return mods;
}
// 处理传的key字符串转换成数组
function getKeys(key) {
if (!key) key = '';
key = key.replace(/\s/g, ''); // 匹配任何空白字符,包括空格、制表符、换页符等等
var keys = key.split(','); // 同时设置多个快捷键,以','分割
var index = keys.lastIndexOf('');
// 快捷键可能包含',',需特殊处理
for (; index >= 0;) {
keys[index - 1] += ',';
keys.splice(index, 1);
index = keys.lastIndexOf('');
}
return keys;
}
// 比较修饰键的数组
function compareArray(a1, a2) {
var arr1 = a1.length >= a2.length ? a1 : a2;
var arr2 = a1.length >= a2.length ? a2 : a1;
var isIndex = true;
for (var i = 0; i < arr1.length; i++) {
if (arr2.indexOf(arr1[i]) === -1) isIndex = false;
}
return isIndex;
}
var _keyMap = { // 特殊键
backspace: 8,
tab: 9,
clear: 12,
enter: 13,
return: 13,
esc: 27,
escape: 27,
space: 32,
left: 37,
up: 38,
right: 39,
down: 40,
del: 46,
delete: 46,
ins: 45,
insert: 45,
home: 36,
end: 35,
pageup: 33,
pagedown: 34,
capslock: 20,
'⇪': 20,
',': 188,
'.': 190,
'/': 191,
'`': 192,
'-': isff ? 173 : 189,
'=': isff ? 61 : 187,
';': isff ? 59 : 186,
'\'': 222,
'[': 219,
']': 221,
'\\': 220
};
var _modifier = { // 修饰键
'⇧': 16,
shift: 16,
'⌥': 18,
alt: 18,
option: 18,
'⌃': 17,
ctrl: 17,
control: 17,
'⌘': isff ? 224 : 91,
cmd: isff ? 224 : 91,
command: isff ? 224 : 91
};
var _downKeys = []; // 记录摁下的绑定键
var modifierMap = {
16: 'shiftKey',
18: 'altKey',
17: 'ctrlKey'
};
var _mods = { 16: false, 18: false, 17: false };
var _handlers = {};
// F1~F12 特殊键
for (var k = 1; k < 20; k++) {
_keyMap['f' + k] = 111 + k;
}
// 兼容Firefox处理
modifierMap[isff ? 224 : 91] = 'metaKey';
_mods[isff ? 224 : 91] = false;
var _scope = 'all'; // 默认热键范围
var isBindElement = false; // 是否绑定节点
// 返回键码
var code = function code(x) {
return _keyMap[x.toLowerCase()] || _modifier[x.toLowerCase()] || x.toUpperCase().charCodeAt(0);
};
// 设置获取当前范围(默认为'所有')
function setScope(scope) {
_scope = scope || 'all';
}
// 获取当前范围
function getScope() {
return _scope || 'all';
}
// 获取摁下绑定键的键值
function getPressedKeyCodes() {
return _downKeys.slice(0);
}
// 表单控件控件判断 返回 Boolean
function filter(event) {
var target = event.target || event.srcElement;
var tagName = target.tagName;
// 忽略这些情况下快捷键无效
return !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA' || target.isContentEditable);
}
// 判断摁下的键是否为某个键,返回true或者false
function isPressed(keyCode) {
if (typeof keyCode === 'string') {
keyCode = code(keyCode); // 转换成键码
}
return _downKeys.indexOf(keyCode) !== -1;
}
// 循环删除handlers中的所有 scope(范围)
function deleteScope(scope, newScope) {
var handlers = void 0;
var i = void 0;
// 没有指定scope,获取scope
if (!scope) scope = getScope();
for (var key in _handlers) {
if (Object.prototype.hasOwnProperty.call(_handlers, key)) {
handlers = _handlers[key];
for (i = 0; i < handlers.length;) {
if (handlers[i].scope === scope) handlers.splice(i, 1);else i++;
}
}
}
// 如果scope被删除,将scope重置为all
if (getScope() === scope) setScope(newScope || 'all');
}
// 清除修饰键
function clearModifier(event) {
var key = event.keyCode || event.which || event.charCode;
var i = _downKeys.indexOf(key);
// 从列表中清除按压过的键
if (i >= 0) {
_downKeys.splice(i, 1);
}
// 特殊处理 cmmand 键,在 cmmand 组合快捷键 keyup 只执行一次的问题
if (event.key && event.key.toLowerCase() === 'meta') {
_downKeys.splice(0, _downKeys.length);
}
// 修饰键 shiftKey altKey ctrlKey (command||metaKey) 清除
if (key === 93 || key === 224) key = 91;
if (key in _mods) {
_mods[key] = false;
// 将修饰键重置为false
for (var k in _modifier) {
if (_modifier[k] === key) hotkeys[k] = false;
}
}
}
// 解除绑定某个范围的快捷键
function unbind(key, scope, method) {
var multipleKeys = getKeys(key);
var keys = void 0;
var mods = [];
var obj = void 0;
// 通过函数判断,是否解除绑定
// https://github.com/jaywcjlove/hotkeys/issues/44
if (typeof scope === 'function') {
method = scope;
scope = 'all';
}
for (var i = 0; i < multipleKeys.length; i++) {
// 将组合快捷键拆分为数组
keys = multipleKeys[i].split('+');
// 记录每个组合键中的修饰键的键码 返回数组
if (keys.length > 1) mods = getMods(_modifier, keys);
// 获取除修饰键外的键值key
key = keys[keys.length - 1];
key = key === '*' ? '*' : code(key);
// 判断是否传入范围,没有就获取范围
if (!scope) scope = getScope();
// 如何key不在 _handlers 中返回不做处理
if (!_handlers[key]) return;
// 清空 handlers 中数据,
// 让触发快捷键键之后没有事件执行到达解除快捷键绑定的目的
for (var r = 0; r < _handlers[key].length; r++) {
obj = _handlers[key][r];
// 通过函数判断,是否解除绑定,函数相等直接返回
var isMatchingMethod = method ? obj.method === method : true;
// 判断是否在范围内并且键值相同
if (isMatchingMethod && obj.scope === scope && compareArray(obj.mods, mods)) {
_handlers[key][r] = {};
}
}
}
}
// 对监听对应快捷键的回调函数进行处理
function eventHandler(event, handler, scope) {
var modifiersMatch = void 0;
// 看它是否在当前范围
if (handler.scope === scope || handler.scope === 'all') {
// 检查是否匹配修饰符(如果有返回true)
modifiersMatch = handler.mods.length > 0;
for (var y in _mods) {
if (Object.prototype.hasOwnProperty.call(_mods, y)) {
if (!_mods[y] && handler.mods.indexOf(+y) > -1 || _mods[y] && handler.mods.indexOf(+y) === -1) modifiersMatch = false;
}
}
// 调用处理程序,如果是修饰键不做处理
if (handler.mods.length === 0 && !_mods[16] && !_mods[18] && !_mods[17] && !_mods[91] || modifiersMatch || handler.shortcut === '*') {
if (handler.method(event, handler) === false) {
if (event.preventDefault) event.preventDefault();else event.returnValue = false;
if (event.stopPropagation) event.stopPropagation();
if (event.cancelBubble) event.cancelBubble = true;
}
}
}
}
// 处理keydown事件
function dispatch(event) {
var asterisk = _handlers['*'];
var key = event.keyCode || event.which || event.charCode;
// 搜集绑定的键
if (_downKeys.indexOf(key) === -1) _downKeys.push(key);
// Gecko(Firefox)的command键值224,在Webkit(Chrome)中保持一致
// Webkit左右command键值不一样
if (key === 93 || key === 224) key = 91;
if (key in _mods) {
_mods[key] = true;
// 将特殊字符的key注册到 hotkeys 上
for (var k in _modifier) {
if (_modifier[k] === key) hotkeys[k] = true;
}
if (!asterisk) return;
}
// 将modifierMap里面的修饰键绑定到event中
for (var e in _mods) {
if (Object.prototype.hasOwnProperty.call(_mods, e)) {
_mods[e] = event[modifierMap[e]];
}
}
// 表单控件过滤 默认表单控件不触发快捷键
if (!hotkeys.filter.call(this, event)) return;
// 获取范围 默认为all
var scope = getScope();
// 对任何快捷键都需要做的处理
if (asterisk) {
for (var i = 0; i < asterisk.length; i++) {
if (asterisk[i].scope === scope && (event.type === 'keydown' && !asterisk[i].keyup || event.type === 'keyup' && asterisk[i].keyup)) {
eventHandler(event, asterisk[i], scope);
}
}
}
// key 不在_handlers中返回
if (!(key in _handlers)) return;
for (var _i = 0; _i < _handlers[key].length; _i++) {
if (event.type === 'keydown' && !_handlers[key][_i].keyup || event.type === 'keyup' && _handlers[key][_i].keyup) {
if (_handlers[key][_i].key) {
var keyShortcut = _handlers[key][_i].key.split('+');
var _downKeysCurrent = []; // 记录当前按键键值
for (var a = 0; a < keyShortcut.length; a++) {
_downKeysCurrent.push(code(keyShortcut[a]));
}
_downKeysCurrent = _downKeysCurrent.sort();
if (_downKeysCurrent.join('') === _downKeys.sort().join('')) {
// 找到处理内容
eventHandler(event, _handlers[key][_i], scope);
}
}
}
}
}
function hotkeys(key, option, method) {
var keys = getKeys(key); // 需要处理的快捷键列表
var mods = [];
var scope = 'all'; // scope默认为all,所有范围都有效
var element = document; // 快捷键事件绑定节点
var i = 0;
// 对为设定范围的判断
if (method === undefined && typeof option === 'function') {
method = option;
}
if (Object.prototype.toString.call(option) === '[object Object]') {
if (option.scope) scope = option.scope; // eslint-disable-line
if (option.element) element = option.element; // eslint-disable-line
}
if (typeof option === 'string') scope = option;
// 对于每个快捷键进行处理
for (; i < keys.length; i++) {
key = keys[i].split('+'); // 按键列表
mods = [];
// 如果是组合快捷键取得组合快捷键
if (key.length > 1) mods = getMods(_modifier, key);
// 将非修饰键转化为键码
key = key[key.length - 1];
key = key === '*' ? '*' : code(key); // *表示匹配所有快捷键
// 判断key是否在_handlers中,不在就赋一个空数组
if (!(key in _handlers)) _handlers[key] = [];
_handlers[key].push({
keyup: option.keyup,
scope: scope,
mods: mods,
shortcut: keys[i],
method: method,
key: keys[i]
});
}
// 在全局document上设置快捷键
if (typeof element !== 'undefined' && !isBindElement) {
isBindElement = true;
addEvent(element, 'keydown', function (e) {
dispatch(e);
});
addEvent(element, 'keyup', function (e) {
dispatch(e);
clearModifier(e);
});
}
}
var _api = {
setScope: setScope,
getScope: getScope,
deleteScope: deleteScope,
getPressedKeyCodes: getPressedKeyCodes,
isPressed: isPressed,
filter: filter,
unbind: unbind
};
for (var a in _api) {
if (Object.prototype.hasOwnProperty.call(_api, a)) {
hotkeys[a] = _api[a];
}
}
if (typeof window !== 'undefined') {
var _hotkeys = window.hotkeys;
hotkeys.noConflict = function (deep) {
if (deep && window.hotkeys === hotkeys) {
window.hotkeys = _hotkeys;
}
return hotkeys;
};
window.hotkeys = hotkeys;
}
var css = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host {\n position: fixed;\n top: 0;\n left: 0;\n z-index: var(--layer-1);\n max-width: -webkit-min-content;\n max-width: -moz-min-content;\n max-width: min-content;\n}\n\n:host > ol {\n -webkit-animation: none 0s ease 0s 1 normal none running;\n animation: none 0s ease 0s 1 normal none running;\n -webkit-backface-visibility: visible;\n backface-visibility: visible;\n background: transparent none repeat 0 0 / auto auto padding-box border-box scroll;\n border: medium none currentColor;\n border-collapse: separate;\n -webkit-border-image: none;\n border-image: none;\n border-radius: 0;\n border-spacing: 0;\n bottom: auto;\n box-shadow: none;\n box-sizing: content-box;\n caption-side: top;\n clear: none;\n clip: auto;\n color: #000;\n -webkit-columns: auto;\n -moz-columns: auto;\n columns: auto;\n -webkit-column-count: auto;\n -moz-column-count: auto;\n column-count: auto;\n -webkit-column-fill: balance;\n -moz-column-fill: balance;\n column-fill: balance;\n grid-column-gap: normal;\n -webkit-column-gap: normal;\n -moz-column-gap: normal;\n column-gap: normal;\n -webkit-column-rule: medium none currentColor;\n -moz-column-rule: medium none currentColor;\n column-rule: medium none currentColor;\n -webkit-column-span: 1;\n -moz-column-span: 1;\n column-span: 1;\n -webkit-column-width: auto;\n -moz-column-width: auto;\n column-width: auto;\n content: normal;\n counter-increment: none;\n counter-reset: none;\n cursor: auto;\n direction: ltr;\n display: inline;\n empty-cells: show;\n float: none;\n font-family: serif;\n font-size: medium;\n font-style: normal;\n -webkit-font-feature-settings: normal;\n font-feature-settings: normal;\n font-variant: normal;\n font-weight: normal;\n font-stretch: normal;\n line-height: normal;\n height: auto;\n -webkit-hyphens: none;\n -ms-hyphens: none;\n hyphens: none;\n left: auto;\n letter-spacing: normal;\n list-style: disc outside none;\n margin: 0;\n max-height: none;\n max-width: none;\n min-height: 0;\n min-width: 0;\n opacity: 1;\n orphans: 2;\n outline: medium none invert;\n overflow: visible;\n overflow-x: visible;\n overflow-y: visible;\n padding: 0;\n page-break-after: auto;\n page-break-before: auto;\n page-break-inside: auto;\n -webkit-perspective: none;\n perspective: none;\n -webkit-perspective-origin: 50% 50%;\n perspective-origin: 50% 50%;\n position: static;\n right: auto;\n -moz-tab-size: 8;\n tab-size: 8;\n table-layout: auto;\n text-align: left;\n -moz-text-align-last: auto;\n text-align-last: auto;\n text-decoration: none;\n text-indent: 0;\n text-shadow: none;\n text-transform: none;\n top: auto;\n -webkit-transform: none;\n transform: none;\n -webkit-transform-origin: 50% 50% 0;\n transform-origin: 50% 50% 0;\n -webkit-transform-style: flat;\n transform-style: flat;\n -webkit-transition: none 0s ease 0s;\n transition: none 0s ease 0s;\n unicode-bidi: normal;\n vertical-align: baseline;\n visibility: visible;\n white-space: normal;\n widows: 2;\n width: auto;\n word-spacing: normal;\n z-index: auto;\n all: initial;\n font-size: 16px;\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\n display: -webkit-box;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n flex-direction: column;\n margin: 1em 0 0.5em 1em;\n padding: 0;\n list-style-type: none;\n border-radius: 2em\n}\n\n:host > ol:not([colors]) {\n box-shadow: 0 0.25em 0.5em hsla(0,0%,0%,10%);\n background: var(--theme-bg);\n }\n\n:host li {\n height: 2.25em;\n width: 2.25em;\n margin: 0.05em 0.25em;\n display: -webkit-inline-box;\n display: inline-flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: center;\n justify-content: center;\n position: relative;\n border-radius: 50%\n}\n\n:host li:first-child { margin-top: 0.25em; }\n\n:host li:last-child { margin-bottom: 0.25em; }\n\n:host li[data-tool]:hover {\n cursor: pointer;\n background-color: var(--theme-icon_hover-bg);\n }\n\n:host li[data-tool]:active {\n background-color: hsl(0,0%,90%);\n }\n\n:host li[data-active=true] > svg:not(.icon-cursor) {\n fill: var(--theme-color);\n }\n\n:host li[data-active=true] > .icon-cursor {\n stroke: var(--theme-color);\n }\n\n@media (max-height: 768px) {\n :host li:nth-of-type(10) > aside, :host li:nth-of-type(11) > aside, :host li:nth-of-type(12) > aside, :host li:nth-of-type(13) > aside {\n top: auto;\n }\n }\n\n:host li > aside {\n overflow: hidden;\n position: absolute;\n direction: ltr;\n text-align: left;\n left: 3em;\n top: 0;\n z-index: -2;\n pointer-events: none;\n background: white;\n color: hsl(0,0%,30%);\n box-shadow: 0 0.1em 4.5em hsla(0,0%,0%,15%);\n border-radius: 1em;\n\n -webkit-transition: opacity 0.3s ease, -webkit-transform 0.2s ease;\n\n transition: opacity 0.3s ease, -webkit-transform 0.2s ease;\n\n transition: opacity 0.3s ease, transform 0.2s ease;\n\n transition: opacity 0.3s ease, transform 0.2s ease, -webkit-transform 0.2s ease;\n opacity: 0;\n -webkit-transform: translateX(-1em);\n transform: translateX(-1em);\n will-change: transform, opacity\n }\n\n:host li > aside > figure {\n margin: 0;\n display: grid;\n }\n\n:host li > aside figcaption {\n padding: 1em;\n display: grid;\n grid-gap: 0.5em;\n gap: 0.5em\n }\n\n:host li > aside figcaption > h2, :host li > aside figcaption > p {\n margin: 0;\n }\n\n:host li > aside figcaption > h2 {\n font-size: 1.5em;\n line-height: 1.1;\n margin-bottom: 0.5em;\n display: grid;\n grid-auto-flow: column;\n -webkit-box-pack: justify;\n justify-content: space-between;\n -webkit-box-align: center;\n align-items: center;\n }\n\n:host li > aside figcaption > p {\n font-size: 1em;\n line-height: 1.5;\n padding-right: 3em;\n }\n\n:host li > aside figcaption [table] {\n display: grid;\n grid-gap: 0.5em;\n gap: 0.5em\n }\n\n:host li > aside figcaption [table] > div {\n display: grid;\n grid-auto-flow: column;\n grid-template-columns: 1fr auto;\n -webkit-box-pack: justify;\n justify-content: space-between;\n }\n\n:host li > aside [hotkey] {\n border-radius: 5em;\n height: 1.5em;\n width: 1.5em;\n display: -webkit-inline-box;\n display: inline-flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: center;\n justify-content: center;\n border: 1px solid var(--theme-color);\n color: var(--theme-color);\n font-weight: 300;\n font-size: 0.5em;\n text-transform: uppercase;\n }\n\n:host li:hover:not([data-tool=\"search\"]) > aside,\n :host li[data-tool=\"search\"] > svg:hover + aside {\n opacity: 1;\n -webkit-transform: translateX(0);\n transform: translateX(0);\n -webkit-transition-delay: 0.75s;\n transition-delay: 0.75s;\n }\n\n:host li input::-webkit-calendar-picker-indicator {\n background: inherit;\n color: var(--theme-color);\n }\n\n:host [colors] {\n margin-top: 0;\n}\n\n:host [colors] > li {\n overflow: hidden;\n border-radius: 50%;\n box-shadow: 0 0 0 2px white, 0 0.25em 0.5em hsla(0,0%,0%,25%);\n background: var(--contextual_color);\n margin-bottom: 0.5em\n}\n\n:host [colors] > li:first-child {\n margin-top: 0;\n }\n\n:host [colors] li:hover:after {\n top: 0;\n}\n\n:host li > svg {\n width: 50%;\n fill: var(--theme-icon_color);\n}\n\n:host li > svg.icon-cursor {\n width: 35%;\n fill: white;\n stroke: var(--theme-icon_color);\n stroke-width: 2px;\n}\n\n:host li[data-tool=\"search\"][data-active=\"true\"]:before {\n -webkit-transform: translateX(-1em);\n transform: translateX(-1em);\n opacity: 0;\n }\n\n:host li[data-tool=\"search\"] > .search {\n position: absolute;\n left: calc(100% - 1.25em);\n top: 0;\n height: 100%;\n z-index: -1;\n box-shadow: 0 0.25em 0.5em hsla(0,0%,0%,10%);\n border-radius: 0 2em 2em 0;\n overflow: hidden;\n}\n\n:host li[data-tool=\"search\"] > .search > input {\n direction: ltr;\n border: none;\n font-size: 1em;\n padding: 0.4em 0.4em 0.4em 2em;\n outline: none;\n height: 100%;\n width: 250px;\n box-sizing: border-box;\n caret-color: hotpink\n}\n\n:host li[data-tool=\"search\"] > .search > input::-webkit-input-placeholder {\n font-weight: lighter;\n font-size: 0.8em;\n }\n\n:host li[data-tool=\"search\"] > .search > input::-moz-placeholder {\n font-weight: lighter;\n font-size: 0.8em;\n }\n\n:host li[data-tool=\"search\"] > .search > input::-ms-input-placeholder {\n font-weight: lighter;\n font-size: 0.8em;\n }\n\n:host li[data-tool=\"search\"] > .search > input::placeholder {\n font-weight: lighter;\n font-size: 0.8em;\n }\n\n:host [colors] > li > svg {\n fill: var(--icon_color);\n mix-blend-mode: luminosity;\n -webkit-filter: brightness(0.5);\n filter: brightness(0.5);\n}\n\n:host [colors] > li > svg > rect:last-child {\n stroke: hsla(0,0%,0%,20%);\n stroke-width: 0.5px;\n}\n\n:host input[type='color'] {\n opacity: 0.01;\n position: absolute;\n top: 0; left: 0;\n width: 100%; height: 100%;\n z-index: 1;\n box-sizing: border-box;\n border: white;\n padding: 0;\n cursor: pointer;\n}\n\n:host input[type='color']:focus {\n outline: none;\n}\n\n:host input[type='color']::-webkit-color-swatch-wrapper {\n padding: 0;\n}\n\n:host input[type='color']::-webkit-color-swatch {\n border: none;\n}\n\n:host input[type='color'][value='']::-webkit-color-swatch {\n background-color: transparent !important;\n background-image: linear-gradient(155deg, #ffffff 0%,#ffffff 46%,#ff0000 46%,#ff0000 54%,#ffffff 55%,#ffffff 100%);\n}\n";
var css$1 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host > svg {\n position: absolute;\n top: var(--top);\n left: var(--left);\n overflow: visible;\n pointer-events: none;\n z-index: var(--layer-3);\n}\n";
var css$2 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host rect {\n width: 100%;\n height: 100%;\n vector-effect: non-scaling-stroke;\n stroke: hsl(267, 100%, 58%);\n stroke-width: 1px;\n fill: none;\n}\n\n:host > svg {\n z-index: var(--layer-5);\n}\n";
var css$3 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host {\n --line-color: var(--theme-purple);\n --line-base: var(--theme-color);\n --line-width: 1px;\n --distance-h: 5px;\n --distance-w: 5px;\n --line-w: 1px;\n --line-h: 1px;\n font-size: 16px;\n}\n\n:host > figure {\n margin: 0;\n position: absolute;\n height: var(--distance-h);\n width: var(--distance-w);\n top: var(--top);\n left: var(--left);\n right: var(--right);\n overflow: visible;\n pointer-events: none;\n z-index: var(--layer-3);\n display: -webkit-box;\n display: flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n flex-direction: var(--direction);\n}\n\n:host > figure figcaption {\n color: white;\n text-shadow: 0 0.5px 0 hsla(0, 0%, 0%, 0.4);\n box-shadow: 0 0.5px 0 hsla(0, 0%, 0%, 0.4);\n background: var(--line-color);\n border-radius: 1em;\n text-align: center;\n line-height: 1.1;\n font-size: 0.7em;\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\n padding: 0.25em 0.5em 0.275em;\n -webkit-font-feature-settings: proportional-num oldstyle-nums stacked-fractions slashed-zero;\n font-feature-settings: proportional-num oldstyle-nums stacked-fractions slashed-zero;\n font-variant-numeric: proportional-num oldstyle-nums stacked-fractions slashed-zero;\n}\n\n:host > figure span {\n background: var(--line-color);\n height: var(--line-h);\n width: var(--line-w);\n}\n\n:host > figure div {\n -webkit-box-flex: 2;\n flex: 2;\n background: var(--line-color);\n width: var(--line-w);\n height: var(--line-h);\n}\n\n:host figure[quadrant=\"bottom\"] > div:first-of-type, :host figure[quadrant=\"right\"] > div:first-of-type, :host figure[quadrant=\"top\"] > div:last-of-type, :host figure[quadrant=\"left\"] > div:last-of-type {\n background: -webkit-gradient(linear, , from(hotpink), to(var(--line-color)));\n background: linear-gradient(to var(--quadrant), hotpink 0%, var(--line-color) 100%);\n}\n";
var css$4 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host {\n --color: hotpink;\n}\n\n:host > svg {\n position:absolute;\n top:0;\n left:0;\n overflow:visible;\n pointer-events:none;\n z-index:var(--layer-5);\n}\n\n:host rect, \n:host line {\n stroke: var(--color);\n}\n\n:host line {\n stroke-dasharray: 2;\n stroke-dasharray-offset: 3;\n}\n";
var css$5 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host {\n font-size: 16px;\n\n --top: 0;\n --left: 0;\n --max-width: 0;\n}\n\n:host > span {\n position: absolute;\n top: var(--top);\n left: var(--left);\n max-width: var(--max-width);\n z-index: var(--layer-4);\n -webkit-transform: translateY(-100%);\n transform: translateY(-100%);\n background: hotpink;\n background: var(--label-bg, hotpink);\n border-radius: 0.2em 0.2em 0 0;\n text-shadow: 0 0.5px 0 hsla(0, 0%, 0%, 0.4);\n color: white;\n display: -webkit-inline-box;\n display: inline-flex;\n -webkit-box-pack: center;\n justify-content: center;\n font-size: 0.8em;\n font-weight: normal;\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\n white-space: nowrap;\n padding: 0.25em 0.4em 0.15em;\n line-height: 1.1;\n}\n\n:host a {\n text-decoration: none;\n color: inherit;\n cursor: pointer;\n text-overflow: ellipsis;\n overflow: hidden\n}\n\n:host a:hover {\n text-decoration: underline;\n color: white;\n }\n\n:host a[node]:before {\n content: \"\\003c\";\n }\n\n:host a[node]:after {\n content: \"\\003e\";\n }\n";
var css$6 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host svg {\n display: none;\n position: absolute;\n top: var(--top);\n left: var(--left);\n overflow: visible;\n pointer-events: none;\n z-index: var(--layer-5)\n}\n\n:host svg > rect {\n fill: hsla(330, 100%, 71%, 0.5);\n width: 100%;\n height: 100%;\n }\n";
var css$7 = ":host [mask] {\n pointer-events: none;\n position: absolute;\n z-index: var(--layer-5);\n width: var(--width);\n height: var(--height);\n top: var(--top);\n left: var(--left);\n background-color: var(--bg);\n -webkit-clip-path: polygon(\n 0% 0%, 0% 100%, var(--target-left) 100%,\n var(--target-left) var(--target-top),\n var(--offset-right) var(--target-top),\n var(--offset-right) var(--offset-bottom),\n 0 var(--offset-bottom), 0 100%,\n 100% 100%, 100% 0%\n );\n clip-path: polygon(\n 0% 0%, 0% 100%, var(--target-left) 100%,\n var(--target-left) var(--target-top),\n var(--offset-right) var(--target-top),\n var(--offset-right) var(--offset-bottom),\n 0 var(--offset-bottom), 0 100%,\n 100% 100%, 100% 0%\n );\n}";
var css$8 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host {\n position: absolute;\n z-index: var(--layer-top);\n\n --arrow-width: 15px;\n --arrow-height: 8px;\n\n --shadow-up: 5px;\n --shadow-down: -5px;\n --shadow-direction: var(--shadow-up);\n\n --arrow-up: polygon(0 0, 100% 0, 50% 100%);\n --arrow-down: polygon(50% 0, 0 100%, 100% 100%);\n --arrow: var(--arrow-up);\n}\n\n:host figure {\n -webkit-animation: none 0s ease 0s 1 normal none running;\n animation: none 0s ease 0s 1 normal none running;\n -webkit-backface-visibility: visible;\n backface-visibility: visible;\n background: transparent none repeat 0 0 / auto auto padding-box border-box scroll;\n border: medium none currentColor;\n border-collapse: separate;\n -webkit-border-image: none;\n border-image: none;\n border-radius: 0;\n border-spacing: 0;\n bottom: auto;\n box-shadow: none;\n box-sizing: content-box;\n caption-side: top;\n clear: none;\n clip: auto;\n color: #000;\n -webkit-columns: auto;\n -moz-columns: auto;\n columns: auto;\n -webkit-column-count: auto;\n -moz-column-count: auto;\n column-count: auto;\n -webkit-column-fill: balance;\n -moz-column-fill: balance;\n column-fill: balance;\n grid-column-gap: normal;\n -webkit-column-gap: normal;\n -moz-column-gap: normal;\n column-gap: normal;\n -webkit-column-rule: medium none currentColor;\n -moz-column-rule: medium none currentColor;\n column-rule: medium none currentColor;\n -webkit-column-span: 1;\n -moz-column-span: 1;\n column-span: 1;\n -webkit-column-width: auto;\n -moz-column-width: auto;\n column-width: auto;\n content: normal;\n counter-increment: none;\n counter-reset: none;\n cursor: auto;\n direction: ltr;\n display: inline;\n empty-cells: show;\n float: none;\n font-family: serif;\n font-size: medium;\n font-style: normal;\n -webkit-font-feature-settings: normal;\n font-feature-settings: normal;\n font-variant: normal;\n font-weight: normal;\n font-stretch: normal;\n line-height: normal;\n height: auto;\n -webkit-hyphens: none;\n -ms-hyphens: none;\n hyphens: none;\n left: auto;\n letter-spacing: normal;\n list-style: disc outside none;\n margin: 0;\n max-height: none;\n max-width: none;\n min-height: 0;\n min-width: 0;\n opacity: 1;\n orphans: 2;\n outline: medium none invert;\n overflow: visible;\n overflow-x: visible;\n overflow-y: visible;\n padding: 0;\n page-break-after: auto;\n page-break-before: auto;\n page-break-inside: auto;\n -webkit-perspective: none;\n perspective: none;\n -webkit-perspective-origin: 50% 50%;\n perspective-origin: 50% 50%;\n position: static;\n right: auto;\n -moz-tab-size: 8;\n tab-size: 8;\n table-layout: auto;\n text-align: left;\n -moz-text-align-last: auto;\n text-align-last: auto;\n text-decoration: none;\n text-indent: 0;\n text-shadow: none;\n text-transform: none;\n top: auto;\n -webkit-transform: none;\n transform: none;\n -webkit-transform-origin: 50% 50% 0;\n transform-origin: 50% 50% 0;\n -webkit-transform-style: flat;\n transform-style: flat;\n -webkit-transition: none 0s ease 0s;\n transition: none 0s ease 0s;\n unicode-bidi: normal;\n vertical-align: baseline;\n visibility: visible;\n white-space: normal;\n widows: 2;\n width: auto;\n word-spacing: normal;\n z-index: auto;\n all: initial;\n direction: ltr;\n font-size: 16px;\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\n max-width: 50vw;\n background: white;\n color: var(--theme-icon_color);\n line-height: initial;\n padding: 0.5em;\n margin: 0;\n display: -webkit-box;\n display: flex;\n -webkit-box-orient: vertical;\n -webkit-box-direction: normal;\n flex-direction: column;\n flex-wrap: nowrap;\n border-radius: 0.25em;\n line-height: initial;\n -webkit-filter: drop-shadow(0 var(--shadow-direction) 0.5em hsla(0,0%,0%,35%));\n filter: drop-shadow(0 var(--shadow-direction) 0.5em hsla(0,0%,0%,35%))\n}\n\n:host figure:after {\n content: \"\";\n background: white;\n width: var(--arrow-width);\n height: var(--arrow-height);\n -webkit-clip-path: var(--arrow);\n clip-path: var(--arrow);\n position: absolute;\n top: var(--arrow-top);\n left: var(--arrow-left);\n }\n\n:host figure a {\n text-decoration: none;\n color: inherit;\n text-overflow: ellipsis;\n overflow: hidden;\n cursor: pointer\n }\n\n:host figure a:hover {\n color: var(--theme-color);\n text-decoration: underline;\n }\n\n:host figure a:empty {\n display: none;\n }\n\n:host figure a[node]:before {\n content: \"\\003c\";\n }\n\n:host figure a[node]:after {\n content: \"\\003e\";\n }\n\n:host h5 {\n display: -webkit-box;\n display: flex;\n font-size: 1em;\n font-weight: bolder;\n margin: 0;\n overflow: hidden;\n white-space: nowrap;\n}\n\n:host h6 {\n margin-top: 1em;\n margin-bottom: 0;\n font-weight: normal;\n}\n\n:host small {\n font-size: 0.7em;\n color: hsl(0,0%,60%)\n}\n\n:host small > span {\n color: hsl(0,0%,20%);\n }\n\n:host a:not(:hover) {\n text-decoration: none;\n}\n\n:host [brand],\n:host [divider] {\n color: var(--theme-color);\n}\n\n:host div {\n display: grid;\n grid-template-columns: auto auto;\n grid-gap: 0.25em 0.5em;\n margin: 0.5em 0 0;\n padding: 0;\n list-style-type: none;\n color: hsl(0,0%,40%);\n font-size: 0.8em;\n font-family: 'Dank Mono', 'Operator Mono', 'Inconsolata', 'Fira Mono', 'SF Mono', 'Monaco', 'Droid Sans Mono', 'Source Code Pro', monospace;\n}\n\n:host [value] {\n color: var(--theme-icon_color);\n display: -webkit-inline-box;\n display: inline-flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: end;\n justify-content: flex-end;\n text-align: right;\n /* white-space: pre; */\n}\n\n:host [text] {\n white-space: normal;\n}\n\n:host [longform] {\n background: var(--theme-icon_hover-bg);\n padding: 0.5em 0.75em;\n border-radius: 0.25em;\n font-family: sans-serif;\n text-align: left;\n line-height: 1.5;\n}\n\n:host [color] {\n position: relative;\n top: 1px;\n display: inline-block;\n width: 1em;\n height: 1em;\n border-radius: 50%;\n margin-right: .5em;\n}\n\n:host [local-modifications] {\n margin-top: 1rem;\n color: var(--theme-color);\n font-weight: bold;\n}\n\n:host [contrast] > span {\n padding: 0 0.5em 0.1em;\n border-radius: 1em;\n box-shadow: 0 0 0 1px hsl(0,0%,90%);\n}\n";
var css$9 = ":host {\n --theme-bg: hsl(0,0%,100%);\n --theme-color: hotpink;\n --theme-blue: hsl(188, 90%, 45%);\n --theme-purple: hsl(267, 100%, 58%);\n --theme-icon_color: hsl(0,0%,20%);\n --theme-icon_hover-bg: hsl(0,0%,95%);\n\n --layer-top: 2147483647;\n --layer-1: 2147483646;\n --layer-2: 2147483645;\n --layer-3: 2147483644;\n --layer-4: 2147483643;\n --layer-5: 2147483642;\n}\n\n:host {\n display: none;\n position: fixed;\n z-index: var(--layer-top);\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: center;\n justify-content: center;\n width: 100vw;\n height: 100vh;\n background: hsl(0,0%,95%);\n font-size: 16px;\n font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif;\n\n --light-grey: hsl(0,0%,90%);\n --grey: hsl(0,0%,60%);\n --dark-grey: hsl(0,0%,40%);\n}\n\n:host [command] {\n padding: 1em;\n text-align: center;\n font-size: 3vw;\n font-weight: lighter;\n letter-spacing: 0.1em;\n color: var(--dark-grey)\n}\n\n:host [command] > [light] {\n color: var(--grey);\n }\n\n:host [command] > [tool] {\n text-decoration: underline;\n -webkit-text-decoration-color: var(--theme-color);\n text-decoration-color: var(--theme-color);\n }\n\n:host [command] > [negative], :host [command] > [side], :host [command] > [amount] {\n font-weight: normal;\n }\n\n:host [card] {\n padding: 1em;\n background: white;\n box-shadow: 0 0.5em 3em hsla(0,0%,0%,20%);\n border-radius: 0.5em;\n color: var(--dark-grey);\n display: -webkit-box;\n display: flex;\n -webkit-box-pack: space-evenly;\n justify-content: space-evenly\n}\n\n:host [card] > div:not([keyboard]) {\n display: -webkit-box;\n display: flex;\n -webkit-box-align: end;\n align-items: flex-end;\n margin-left: 1em;\n }\n\n:host [tool-icon] {\n position: absolute;\n top: 1em;\n left: 0;\n width: 100%;\n padding: 0 1rem;\n display: -webkit-box;\n display: flex;\n -webkit-box-pack: center;\n justify-content: center\n}\n\n:host [tool-icon] > span {\n color: var(--dark-grey);\n display: grid;\n grid-template-columns: 5vmax auto;\n grid-gap: 0.5em;\n -webkit-box-align: center;\n align-items: center;\n text-transform: capitalize;\n font-size: 4vmax;\n font-weight: lighter;\n }\n\n:host [tool-icon] svg {\n width: 100%;\n fill: var(--theme-color);\n }\n\n:host section {\n display: -webkit-box;\n display: flex;\n -webkit-box-pack: center;\n justify-content: center;\n}\n\n:host section > span, \n:host [arrows] > span {\n border: 1px solid transparent;\n border-radius: 0.5em;\n display: -webkit-inline-box;\n display: inline-flex;\n -webkit-box-align: center;\n align-items: center;\n -webkit-box-pack: center;\n justify-content: center;\n margin: 2px;\n padding: 1.5vw;\n font-size: 0.75em;\n white-space: nowrap;\n}\n\n:host section > span:not([pressed=\"true\"]), \n:host [arrows] > span:not([pressed=\"true\"]) {\n border: 1px solid var(--light-grey)\n}\n\n:host section > span:not([pressed=\"true\"]):hover, :host [arrows] > span:not([pressed=\"true\"]):hover {\n border-color: var(--grey);\n }\n\n:host span[pressed=\"true\"] {\n background: var(--theme-color);\n color: white;\n}\n\n:host span:not([pressed=\"true\"])[used] {\n background: var(--light-grey);\n}\n\n:host span[hotkey] {\n color: var(--theme-color);\n font-weight: bold;\n cursor: pointer;\n}\n\n:host section > span[hotkey]:not([pressed=\"true\"]) {\n border-color: var(--theme-color);\n}\n\n:host [arrows] {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n grid-template-rows: 1fr 1fr\n}\n\n:host [arrows] > span:nth-child(1) {\n grid-row: 1;\n grid-column: 2;\n }\n\n:host [arrows] > span:nth-child(2) {\n grid-row: 2;\n grid-column: 2;\n }\n\n:host [arrows] > span:nth-child(3) {\n grid-row: 2;\n grid-column: 1;\n }\n\n:host [arrows] > span:nth-child(4) {\n grid-row: 2;\n grid-column: 3;\n }\n\n:host [caps] > span:nth-child(1),\n:host [shift] > span:nth-child(1) { -webkit-box-pack: start; justify-content: flex-start; }\n\n:host [shift] > span:nth-child(12) { -webkit-box-pack: end; justify-content: flex-end; }";
const constructStylesheet = (styles, stylesheet = new CSSStyleSheet()) => {
stylesheet.replaceSync(styles);
return stylesheet
};
const VisBugStyles = constructStylesheet(css);
const HandleStyles = constructStylesheet(css$1);
const HoverStyles = constructStylesheet(css$2);
const MetatipStyles = constructStylesheet(css$8);
const DistanceStyles = constructStylesheet(css$3);
const GridlineStyles = constructStylesheet(css$4);
const LabelStyles = constructStylesheet(css$5);
const OverlayStyles = constructStylesheet(css$6);
const BoxModelStyles = constructStylesheet(css$7);
const HotkeymapStyles = constructStylesheet(css$9);
class Handles extends HTMLElement {
constructor() {
super();
this.$shadow = this.attachShadow({mode: 'closed'});
this.styles = [HandleStyles];
}
connectedCallback() {
this.$shadow.adoptedStyleSheets = this.styles;
window.addEventListener('resize', this.on_resize.bind(this));
}
disconnectedCallback() {
window.removeEventListener('resize', this.on_resize);
}
on_resize() {
window.requestAnimationFrame(() => {
const node_label_id = this.$shadow.host.getAttribute('data-label-id');
const [source_el] = $(`[data-label-id="${node_label_id}"]`);
if (!source_el) return
this.position = {
node_label_id,
el: source_el,
};
});
}
set position({el, node_label_id}) {
this.$shadow.innerHTML = this.render(el.getBoundingClientRect(), node_label_id);
if (this._backdrop) {
this.backdrop = {
element: this._backdrop.update(el),
update: this._backdrop.update,
};
}
}
set backdrop(bd) {
this._backdrop = bd;
const cur_child = this.$shadow.querySelector('visbug-boxmodel');
cur_child
? this.$shadow.replaceChild(bd.element, cur_child)
: this.$shadow.appendChild(bd.element);
}
render({ x, y, width, height, top, left }, node_label_id) {
this.$shadow.host.setAttribute('data-label-id', node_label_id);
this.style.setProperty('--top', `${top + window.scrollY}px`);
this.style.setProperty('--left', `${left}px`);
return `
<svg
class="visbug-handles"
width="${width}" height="${height}"
viewBox="0 0 ${width} ${height}"
version="1.1" xmlns="http://www.w3.org/2000/svg"
>
<rect stroke="hotpink" fill="none" width="100%" height="100%"></rect>
<circle stroke="hotpink" fill="white" cx="0" cy="0" r="2"></circle>
<circle stroke="hotpink" fill="white" cx="100%" cy="0" r="2"></circle>
<circle stroke="hotpink" fill="white" cx="100%" cy="100%" r="2"></circle>
<circle stroke="hotpink" fill="white" cx="0" cy="100%" r="2"></circle>
<circle fill="hotpink" cx="${width/2}" cy="0" r="2"></circle>
<circle fill="hotpink" cx="0" cy="${height/2}" r="2"></circle>
<circle fill="hotpink" cx="${width/2}" cy="${height}" r="2"></circle>
<circle fill="hotpink" cx="${width}" cy="${height/2}" r="2"></circle>
</svg>
`
}
}
customElements.define('visbug-handles', Handles);
class Hover extends Handles {
constructor() {
super();
this.styles = [HandleStyles, HoverStyles];
}
render({ width, height, top, left }) {
this.style.setProperty('--top', `${top + window.scrollY}px`);
this.style.setProperty('--left', `${left}px`);
return `
<svg width="${width}" height="${height}">
<rect></rect>
</svg>
`
}
}
customElements.define('visbug-hover', Hover);
class Label extends HTMLElement {
constructor() {
super();
this.$shadow = this.attachShadow({mode: 'closed'});
}
connectedCallback() {
this.$shadow.adoptedStyleSheets = [LabelStyles];
$('a', this.$shadow).on('click mouseenter', this.dispatchQuery.bind(this));
window.addEventListener('resize', this.on_resize.bind(this));
}
disconnectedCallback() {
$('a', this.$shadow).off('click', this.dispatchQuery);
window.removeEventListener('resize', this.on_resize);
}
on_resize() {
window.requestAnimationFrame(() => {
const node_label_id = this.$shadow.host.getAttribute('data-label-id');
const [source_el] = $(`[data-label-id="${node_label_id}"]`);
if (!source_el) return
this.position = {
node_label_id,
boundingRect: source_el.getBoundingClientRect(),
};
});
}
dispatchQuery(e) {
this.$shadow.host.dispatchEvent(new CustomEvent('query', {
bubbles: true,
detail: {
text: e.target.textContent,
activator: e.type,
}
}));
}
set text(content) {
this._text = content;
}
set position({boundingRect, node_label_id}) {
this.$shadow.innerHTML = this.render(node_label_id);
this.update = boundingRect;
}
set update({x,y,width}) {
this.style.setProperty('--top', `${y + window.scrollY}px`);
this.style.setProperty('--left', `${x - 1}px`);
this.style.setProperty('--max-width', `${width + (window.innerWidth - x - width - 20)}px`);
}
render(node_label_id) {
this.$shadow.host.setAttribute('data-label-id', node_label_id);
return `<span>${this._text}</span>`
}
}
customElements.define('visbug-label', Label);
const desiredPropMap = {
color: 'rgb(0, 0, 0)',
backgroundColor: 'rgba(0, 0, 0, 0)',
backgroundImage: 'none',
backgroundSize: 'auto',
backgroundPosition: '0% 0%',
// borderColor: 'rgb(0, 0, 0)',
borderWidth: '0px',
borderRadius: '0px',
boxShadow: 'none',
padding: '0px',
margin: '0px',
fontFamily: '',
fontSize: '16px',
fontWeight: '400',
textAlign: 'start',
textShadow: 'none',
textTransform: 'none',
lineHeight: 'normal',
letterSpacing: 'normal',
display: 'block',
alignItems: 'normal',
justifyContent: 'normal',
flexDirection: 'row',
flexWrap: 'nowrap',
flexBasis: 'auto',
// flexFlow: 'none',
fill: 'rgb(0, 0, 0)',
stroke: 'none',
gridTemplateColumns: 'none',
gridAutoColumns: 'auto',
gridTemplateRows: 'none',
gridAutoRows: 'auto',
gridTemplateAreas: 'none',
gridArea: 'auto / auto / auto / auto',
gap: 'normal normal',
gridAutoFlow: 'row',
};
const desiredAccessibilityMap = [
'role',
'tabindex',
'aria-*',
'for',
'alt',
'title',
'type',
];
const largeWCAG2TextMap = [
{
fontSize: '24px',
fontWeight: '0'
},
{
fontSize: '18.5px',
fontWeight: '700'
}
];
const getStyle = (el, name) => {
if (document.defaultView && document.defaultView.getComputedStyle) {
name = name.replace(/([A-Z])/g, '-$1');
name = name.toLowerCase();
let s = document.defaultView.getComputedStyle(el, '');
return s && s.getPropertyValue(name)
}
};
const getStyles = el => {
const elStyleObject = el.style;
const computedStyle = window.getComputedStyle(el, null);
let desiredValues = [];
for (prop in el.style)
if (prop in desiredPropMap && desiredPropMap[prop] != computedStyle[prop])
desiredValues.push({
prop,
value: computedStyle[prop].replace(/, rgba/g, '\rrgba')
});
return desiredValues
};
const getComputedBackgroundColor = el => {
let background = getStyle(el, 'background-color');
if (background === 'rgba(0, 0, 0, 0)') {
let node = findNearestParentElement(el)
, found = false;
while(!found) {
let bg = getStyle(node, 'background-color');
if (bg !== 'rgba(0, 0, 0, 0)') {
found = true;
background = bg;
}
node = findNearestParentElement(node);
if (node.nodeName === 'HTML') {
found = true;
background = 'white';
}
}
}
return background
};
const findNearestParentElement = el =>
el.parentNode && el.parentNode.nodeType === 1
? el.parentNode
: el.parentNode.nodeName === '#document-fragment'
? el.parentNode.host
: el.parentNode.parentNode.host;
const findNearestChildElement = el => {
if (el.shadowRoot && el.shadowRoot.children.length) {
return [...el.shadowRoot.children]
.filter(({nodeName}) =>
!['LINK','STYLE','SCRIPT','HTML','HEAD'].includes(nodeName)
)[0]
}
else if (el.children.length)
return el.children[0]
};
const loadStyles = async stylesheets => {
const fetches = await Promise.all(stylesheets.map(url => fetch(url)));
const texts = await Promise.all(fetches