Socket
Socket
Sign inDemoInstall

vconsole

Package Overview
Dependencies
Maintainers
1
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vconsole - npm Package Compare versions

Comparing version 3.4.1 to 3.5.0

dev/data/success.txt

10

CHANGELOG_CN.md
[English](./CHANGELOG.md) | 简体中文
#### 3.5.0 (2021-04-28)
- `Feature(Log)` 新增复制单行日志的能力。 (by @akai)
- `Feature(Plugin)` 新第三方插件 [vconsole-vue-devtools-plugin](https://github.com/Zippowxk/vue-vconsole-devtools)。 (by @Zippowxk)
- `Perf(System)` 将 "System" 字段改名为 "Client",并新增 `MacOS` 系统版本号。
- `Fix(Log)` 使用自然排序来排序 object 和 array 的键值。 (issue #372)
- `Fix(Network)` 修复当 `contentType` 为 `text/html` 时潜在的 JSON 错误。 (by @zimv)
- `Fix(Network)` 修复 `disableLogScrolling` 参数在 Network 面板中不生效的问题。 (issue #282, #379)
#### V3.4.1 (2021-04-09)

@@ -4,0 +14,0 @@

12

CHANGELOG.md
English | [简体中文](./CHANGELOG_CN.md)
#### 3.5.0 (2021-04-28)
- `Feature(Log)` Add ability to copy a single line of logs. (by @akai)
- `Feature(Plugin)` New third-party plugin [vconsole-vue-devtools-plugin](https://github.com/Zippowxk/vue-vconsole-devtools). (by @Zippowxk)
- `Perf(System)` Rename "System" field to "Client", and add `MacOS` version.
- `Fix(Log)` Use natural sorting to sort object and array's keys. (issue #372)
- `Fix(Network)` Fix JSON parse error when `contentType` is `text/html`. (by @zimv)
- `Fix(Network)` Fix `disableLogScrolling` not working in Network panel. (issue #282, #379)
#### V3.4.1 (2021-04-09)

@@ -22,3 +32,3 @@

- `Feature(Network)` Use short URL and display parameters in Network tab. (issue #291)
- `Feature(Plugin)` New plugin [vconsole-stats-plugin](https://github.com/smackgg/vConsole-Stats). (by @smackgg)
- `Feature(Plugin)` New third-party plugin [vconsole-stats-plugin](https://github.com/smackgg/vConsole-Stats). (by @smackgg)
- `Fix(General)` The position of the switch button will be reset by mistake when clicked.

@@ -25,0 +35,0 @@ - `Fix(General)` Fix `document.documentElement.offsetHeight|offsetWidth` is unreliable in newer browsers. (by @littlee)

23

package.json
{
"name": "vconsole",
"version": "3.4.1",
"version": "3.5.0",
"description": "A lightweight, extendable front-end developer tool for mobile web page.",

@@ -9,3 +9,2 @@ "homepage": "https://github.com/Tencent/vConsole",

"scripts": {
"test": "mocha",
"build": "webpack",

@@ -24,14 +23,15 @@ "dev": "webpack-dev-server --config webpack.dev.config"

"dependencies": {
"core-js": "^3.8.3",
"copy-text-to-clipboard": "^3.0.1",
"core-js": "^3.11.0",
"mutation-observer": "^1.0.3"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-proposal-export-namespace-from": "^7.12.1",
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
"@babel/preset-env": "^7.12.11",
"@babel/core": "^7.13.16",
"@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-export-namespace-from": "^7.12.13",
"@babel/plugin-proposal-object-rest-spread": "^7.13.8",
"@babel/preset-env": "^7.13.15",
"babel-loader": "^8.2.2",
"babel-plugin-add-module-exports": "^1.0.4",
"chai": "^4.2.0",
"chai": "^4.3.4",
"copy-webpack-plugin": "^5.1.2",

@@ -44,7 +44,6 @@ "css-loader": "^3.6.0",

"less-loader": "^5.0.0",
"mocha": "^5.2.0",
"style-loader": "^1.3.0",
"webpack": "^4.44.2",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^4.2.2"

@@ -51,0 +50,0 @@ },

@@ -77,3 +77,3 @@ [English](./README.md) | 简体中文

## 插件列表
## 第三方插件列表

@@ -83,2 +83,4 @@ - [vConsole-sources](https://github.com/WechatFE/vConsole-sources)

- [vconsole-stats-plugin](https://github.com/smackgg/vConsole-Stats)
- [vconsole-vue-devtools-plugin](https://github.com/Zippowxk/vue-vconsole-devtools)
- [vconsole-outputlog-plugin](https://github.com/sunlanda/vconsole-outputlog-plugin)

@@ -85,0 +87,0 @@ ## 更新记录

@@ -74,3 +74,3 @@ English | [简体中文](./README_CN.md)

## Plugins
## Third-party Plugins

@@ -80,2 +80,4 @@ - [vConsole-sources](https://github.com/WechatFE/vConsole-sources)

- [vconsole-stats-plugin](https://github.com/smackgg/vConsole-Stats)
- [vconsole-vue-devtools-plugin](https://github.com/Zippowxk/vue-vconsole-devtools)
- [vconsole-outputlog-plugin](https://github.com/sunlanda/vconsole-outputlog-plugin)

@@ -82,0 +84,0 @@ ## Changelog

@@ -16,786 +16,774 @@ /*

import 'transitionEnd'
import pkg from '../../package.json';
import * as tool from '../lib/tool.js';
import $ from '../lib/query.js';
import './core.less';
import tpl from './core.html';
import tplTabbar from './tabbar.html';
import tplTabbox from './tabbox.html';
import tplTopBarItem from './topbar_item.html';
import tplToolItem from './tool_item.html';
// built-in plugins
import VConsolePlugin from '../lib/plugin.js';
import VConsoleLogPlugin from '../log/log.js';
import VConsoleDefaultPlugin from '../log/default.js';
import VConsoleSystemPlugin from '../log/system.js';
import VConsoleNetworkPlugin from '../network/network.js';
import VConsoleElementPlugin from '../element/element.js';
import VConsoleStoragePlugin from '../storage/storage.js';
const VCONSOLE_ID = '#__vconsole';
class VConsole {
constructor(opt) {
if (!!$.one(VCONSOLE_ID)) {
console.debug('vConsole is already exists.');
return;
}
let that = this;
this.version = pkg.version;
this.$dom = null;
this.isInited = false;
this.option = {
defaultPlugins: ['system', 'network', 'element', 'storage']
};
this.activedTab = '';
this.tabList = [];
this.pluginList = {};
this.switchPos = {
hasMoved: false, // exclude click event
x: 0, // right
y: 0, // bottom
startX: 0,
startY: 0,
endX: 0,
endY: 0
};
// export helper functions to public
this.tool = tool;
this.$ = $;
// merge options
if (tool.isObject(opt)) {
for (let key in opt) {
this.option[key] = opt[key];
}
}
// add built-in plugins
this._addBuiltInPlugins();
// try to init
let _onload = function() {
if (that.isInited) {
return;
}
that._render();
that._mockTap();
that._bindEvent();
that._autoRun();
};
if (document !== undefined) {
if (document.readyState === 'loading') {
$.bind(window, 'DOMContentLoaded', _onload);
} else {
_onload();
}
} else {
// if document does not exist, wait for it
let _timer;
let _pollingDocument = function() {
if (!!document && document.readyState == 'complete') {
_timer && clearTimeout(_timer);
_onload();
} else {
_timer = setTimeout(_pollingDocument, 1);
}
};
_timer = setTimeout(_pollingDocument, 1);
}
}
/**
* add built-in plugins
*/
_addBuiltInPlugins() {
// add default log plugin
this.addPlugin(new VConsoleDefaultPlugin('default', 'Log'));
// add other built-in plugins according to user's config
const list = this.option.defaultPlugins;
const plugins = {
'system': {proto: VConsoleSystemPlugin, name: 'System'},
'network': {proto: VConsoleNetworkPlugin, name: 'Network'},
'element': {proto: VConsoleElementPlugin, name: 'Element'},
'storage': {proto: VConsoleStoragePlugin, name: 'Storage'}
};
if (!!list && tool.isArray(list)) {
for (let i=0; i<list.length; i++) {
let tab = plugins[list[i]];
if (!!tab) {
this.addPlugin(new tab.proto(list[i], tab.name));
} else {
console.debug('Unrecognized default plugin ID:', list[i]);
}
}
}
}
/**
* render panel DOM
* @private
*/
_render() {
if (! $.one(VCONSOLE_ID)) {
const e = document.createElement('div');
e.innerHTML = tpl;
document.documentElement.insertAdjacentElement('beforeend', e.children[0]);
}
this.$dom = $.one(VCONSOLE_ID);
// reposition switch button
const $switch = $.one('.vc-switch', this.$dom);
let switchX = tool.getStorage('switch_x') * 1,
switchY = tool.getStorage('switch_y') * 1;
this.setSwitchPosition(switchX, switchY);
// modify font-size
const dpr = window.devicePixelRatio || 1;
const viewportEl = document.querySelector('[name="viewport"]');
if (viewportEl && viewportEl.content) {
const initialScale = viewportEl.content.match(/initial\-scale\=\d+(\.\d+)?/);
const scale = initialScale ? parseFloat(initialScale[0].split('=')[1]) : 1;
if (scale < 1) {
this.$dom.style.fontSize = 13 * dpr + 'px';
}
}
// remove from less to present transition effect
$.one('.vc-mask', this.$dom).style.display = 'none';
// set theme
this._updateTheme();
}
/**
* Update theme
* @private
*/
_updateTheme() {
const theme = this.option.theme || 'light';
this.$dom.setAttribute('data-theme', theme);
}
setSwitchPosition(switchX, switchY) {
const $switch = $.one('.vc-switch', this.$dom);
[switchX, switchY] = this._getSwitchButtonSafeAreaXY($switch, switchX, switchY);
this.switchPos.x = switchX;
this.switchPos.y = switchY;
$switch.style.right = switchX + 'px';
$switch.style.bottom = switchY + 'px';
tool.setStorage('switch_x', switchX);
tool.setStorage('switch_y', switchY);
}
/**
* Get an safe [x, y] position for switch button
* @private
*/
_getSwitchButtonSafeAreaXY($switch, x, y) {
const docWidth = Math.max(document.documentElement.offsetWidth, window.innerWidth);
const docHeight = Math.max(document.documentElement.offsetHeight, window.innerHeight);
// check edge
if (x + $switch.offsetWidth > docWidth) {
x = docWidth - $switch.offsetWidth;
}
if (y + $switch.offsetHeight > docHeight) {
y = docHeight - $switch.offsetHeight;
}
if (x < 0) { x = 0; }
if (y < 20) { y = 20; } // safe area for iOS Home indicator
return [x, y];
}
/**
* Get an safe [x, y] position for switch button
* @private
*/
_getSwitchButtonSafeAreaXY($switch, x, y) {
const docWidth = Math.max(document.documentElement.offsetWidth, window.innerWidth);
const docHeight = Math.max(document.documentElement.offsetHeight, window.innerHeight);
// check edge
if (x + $switch.offsetWidth > docWidth) {
x = docWidth - $switch.offsetWidth;
}
if (y + $switch.offsetHeight > docHeight) {
y = docHeight - $switch.offsetHeight;
}
if (x < 0) { x = 0; }
if (y < 20) { y = 20; } // safe area for iOS Home indicator
return [x, y];
}
/**
* simulate tap event by touchstart & touchend
* @private
*/
_mockTap() {
let tapTime = 700, // maximun tap interval
tapBoundary = 10; // max tap move distance
let lastTouchStartTime,
touchstartX,
touchstartY,
touchHasMoved = false,
targetElem = null;
this.$dom.addEventListener('touchstart', function(e) { // todo: if double click
if (lastTouchStartTime === undefined) {
let touch = e.targetTouches[0];
touchstartX = touch.pageX;
touchstartY = touch.pageY;
lastTouchStartTime = e.timeStamp;
targetElem = (e.target.nodeType === Node.TEXT_NODE ? e.target.parentNode : e.target);
}
}, false);
this.$dom.addEventListener('touchmove', function(e) {
let touch = e.changedTouches[0];
if (Math.abs(touch.pageX - touchstartX) > tapBoundary || Math.abs(touch.pageY - touchstartY) > tapBoundary) {
touchHasMoved = true;
}
});
this.$dom.addEventListener('touchend', function(e) {
// move and time within limits, manually trigger `click` event
if (touchHasMoved === false && e.timeStamp - lastTouchStartTime < tapTime && targetElem != null) {
let tagName = targetElem.tagName.toLowerCase(),
needFocus = false;
switch (tagName) {
case 'textarea': // focus
needFocus = true; break;
case 'input':
switch (targetElem.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
needFocus = false; break;
default:
needFocus = !targetElem.disabled && !targetElem.readOnly;
}
default:
break;
}
if (needFocus) {
targetElem.focus();
} else if (typeof window.getSelection !== 'function' || !getSelection().rangeCount) {
e.preventDefault(); // prevent click 300ms later
}
if (!targetElem.disabled && !targetElem.readOnly) {
let touch = e.changedTouches[0];
let event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
event.forwardedTouchEvent = true;
event.initEvent('click', true, true);
targetElem.dispatchEvent(event);
}
}
// reset values
lastTouchStartTime = undefined;
touchHasMoved = false;
targetElem = null;
}, false);
}
/**
* bind DOM events
* @private
*/
_bindEvent() {
const that = this;
// drag & drop switch button
const $switch = $.one('.vc-switch', that.$dom);
$.bind($switch, 'touchstart', function(e) {
that.switchPos.startX = e.touches[0].pageX;
that.switchPos.startY = e.touches[0].pageY;
that.switchPos.hasMoved = false;
});
$.bind($switch, 'touchend', function(e) {
if (!that.switchPos.hasMoved) {
return;
}
that.switchPos.startX = 0;
that.switchPos.startY = 0;
that.switchPos.hasMoved = false;
that.setSwitchPosition(that.switchPos.endX, that.switchPos.endY);
});
$.bind($switch, 'touchmove', function(e) {
if (e.touches.length <= 0) {
return;
}
const offsetX = e.touches[0].pageX - that.switchPos.startX,
offsetY = e.touches[0].pageY - that.switchPos.startY;
let x = Math.floor(that.switchPos.x - offsetX),
y = Math.floor(that.switchPos.y - offsetY);
[x, y] = that._getSwitchButtonSafeAreaXY($switch, x, y);
$switch.style.right = x + 'px';
$switch.style.bottom = y + 'px';
that.switchPos.endX = x;
that.switchPos.endY = y;
that.switchPos.hasMoved = true;
e.preventDefault();
});
// show console panel
$.bind($.one('.vc-switch', that.$dom), 'click', function() {
that.show();
});
// hide console panel
$.bind($.one('.vc-hide', that.$dom), 'click', function() {
that.hide();
});
// hide console panel when tap background mask
let $mask = $.one('.vc-mask', this.$dom);
let $panel = $.one('.vc-panel', this.$dom);
const transitionEnd = window.transitionEnd($mask).whichTransitionEnd()
const onMaskTransitionEnd = function() {
$mask.style.display = 'none';
$panel.style.display = 'none';
};
if (transitionEnd) {
$.bind($mask, transitionEnd, onMaskTransitionEnd);
} else {
onMaskTransitionEnd();
}
$.bind($mask, 'click', function(e) {
if (e.target != $.one('.vc-mask')) {
return false;
}
that.hide();
});
// show tab box
$.delegate($.one('.vc-tabbar', that.$dom), 'click', '.vc-tab', function(e) {
let tabName = this.dataset.tab;
if (tabName == that.activedTab) {
return;
}
that.showTab(tabName);
});
// disable background scrolling
let $content = $.one('.vc-content', that.$dom);
let preventMove = false;
$.bind($content, 'touchstart', function (e) {
let top = $content.scrollTop,
totalScroll = $content.scrollHeight,
currentScroll = top + $content.offsetHeight;
if (top === 0) {
// when content is on the top,
// reset scrollTop to lower position to prevent iOS apply scroll action to background
$content.scrollTop = 1;
// however, when content's height is less than its container's height,
// scrollTop always equals to 0 (it is always on the top),
// so we need to prevent scroll event manually
if ($content.scrollTop === 0) {
if (!$.hasClass(e.target, 'vc-cmd-input')) { // skip input
preventMove = true;
}
}
} else if (currentScroll === totalScroll) {
// when content is on the bottom,
// do similar processing
$content.scrollTop = top - 1;
if ($content.scrollTop === top) {
if (!$.hasClass(e.target, 'vc-cmd-input')) {
preventMove = true;
}
}
}
});
$.bind($content, 'touchmove', function (e) {
if (preventMove) {
e.preventDefault();
}
});
$.bind($content, 'touchend', function (e) {
preventMove = false;
});
};
/**
* auto run after initialization
* @private
*/
_autoRun() {
this.isInited = true;
// init plugins
for (let id in this.pluginList) {
this._initPlugin(this.pluginList[id]);
}
// show first tab
if (this.tabList.length > 0) {
this.showTab(this.tabList[0]);
}
this.triggerEvent('ready');
}
/**
* trigger a vConsole.option event
* @protect
*/
triggerEvent(eventName, param) {
eventName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
if (tool.isFunction(this.option[eventName])) {
this.option[eventName].apply(this, param);
}
}
/**
* init a plugin
* @private
*/
_initPlugin(plugin) {
let that = this;
plugin.vConsole = this;
// start init
plugin.trigger('init');
// render tab (if it is a tab plugin then it should has tab-related events)
plugin.trigger('renderTab', function(tabboxHTML) {
// add to tabList
that.tabList.push(plugin.id);
// render tabbar
let $tabbar = $.render(tplTabbar, {id: plugin.id, name: plugin.name});
$.one('.vc-tabbar', that.$dom).insertAdjacentElement('beforeend', $tabbar);
// render tabbox
let $tabbox = $.render(tplTabbox, {id: plugin.id});
if (!!tabboxHTML) {
if (tool.isString(tabboxHTML)) {
$tabbox.innerHTML += tabboxHTML;
} else if (tool.isFunction(tabboxHTML.appendTo)) {
tabboxHTML.appendTo($tabbox);
} else if (tool.isElement(tabboxHTML)) {
$tabbox.insertAdjacentElement('beforeend', tabboxHTML);
}
}
$.one('.vc-content', that.$dom).insertAdjacentElement('beforeend', $tabbox);
});
// render top bar
plugin.trigger('addTopBar', function(btnList) {
if (!btnList) {
return;
}
let $topbar = $.one('.vc-topbar', that.$dom);
for (let i=0; i<btnList.length; i++) {
let item = btnList[i];
let $item = $.render(tplTopBarItem, {
name: item.name || 'Undefined',
className: item.className || '',
pluginID: plugin.id
});
if (item.data) {
for (let k in item.data) {
$item.dataset[k] = item.data[k];
}
}
if (tool.isFunction(item.onClick)) {
$.bind($item, 'click', function(e) {
let enable = item.onClick.call($item);
if (enable === false) {
// do nothing
} else {
$.removeClass($.all('.vc-topbar-' + plugin.id), 'vc-actived');
$.addClass($item, 'vc-actived');
}
});
}
$topbar.insertAdjacentElement('beforeend', $item);
}
});
// render tool bar
plugin.trigger('addTool', function(toolList) {
if (!toolList) {
return;
}
let $defaultBtn = $.one('.vc-tool-last', that.$dom);
for (let i=0; i<toolList.length; i++) {
let item = toolList[i];
let $item = $.render(tplToolItem, {
name: item.name || 'Undefined',
pluginID: plugin.id
});
if (item.global == true) {
$.addClass($item, 'vc-global-tool');
}
if (tool.isFunction(item.onClick)) {
$.bind($item, 'click', function(e) {
item.onClick.call($item);
});
}
$defaultBtn.parentNode.insertBefore($item, $defaultBtn);
}
});
// end init
plugin.isReady = true;
plugin.trigger('ready');
}
/**
* trigger an event for each plugin
* @private
*/
_triggerPluginsEvent(eventName) {
for (let id in this.pluginList) {
if (this.pluginList[id].isReady) {
this.pluginList[id].trigger(eventName);
}
}
}
/**
* trigger an event by plugin's name
* @private
*/
_triggerPluginEvent(pluginName, eventName) {
let plugin = this.pluginList[pluginName];
if (!!plugin && plugin.isReady) {
plugin.trigger(eventName);
}
}
/**
* add a new plugin
* @public
* @param object VConsolePlugin object
* @return boolean
*/
addPlugin(plugin) {
// ignore this plugin if it has already been installed
if (this.pluginList[plugin.id] !== undefined) {
console.debug('Plugin ' + plugin.id + ' has already been added.');
return false;
}
this.pluginList[plugin.id] = plugin;
// init plugin only if vConsole is ready
if (this.isInited) {
this._initPlugin(plugin);
// if it's the first plugin, show it by default
if (this.tabList.length == 1) {
this.showTab(this.tabList[0]);
}
}
return true;
}
/**
* remove a plugin
* @public
* @param string pluginID
* @return boolean
*/
removePlugin(pluginID) {
pluginID = (pluginID + '').toLowerCase();
let plugin = this.pluginList[pluginID];
// skip if is has not been installed
if (plugin === undefined) {
console.debug('Plugin ' + pluginID + ' does not exist.');
return false;
}
// trigger `remove` event before uninstall
plugin.trigger('remove');
// the plugin will not be initialized before vConsole is ready,
// so the plugin does not need to handle DOM-related actions in this case
if (this.isInited) {
let $tabbar = $.one('#__vc_tab_' + pluginID);
$tabbar && $tabbar.parentNode.removeChild($tabbar);
// remove topbar
let $topbar = $.all('.vc-topbar-' + pluginID, this.$dom);
for (let i=0; i<$topbar.length; i++) {
$topbar[i].parentNode.removeChild($topbar[i]);
}
// remove content
let $content = $.one('#__vc_log_' + pluginID);
$content && $content.parentNode.removeChild($content);
// remove tool bar
let $toolbar = $.all('.vc-tool-' + pluginID, this.$dom);
for (let i=0; i<$toolbar.length; i++) {
$toolbar[i].parentNode.removeChild($toolbar[i]);
}
}
// remove plugin from list
let index = this.tabList.indexOf(pluginID);
if (index > -1) {
this.tabList.splice(index, 1);
}
try {
delete this.pluginList[pluginID];
} catch (e) {
this.pluginList[pluginID] = undefined;
}
// show the first plugin by default
if (this.activedTab == pluginID) {
if (this.tabList.length > 0) {
this.showTab(this.tabList[0]);
}
}
return true;
}
/**
* show console panel
* @public
*/
show() {
if (!this.isInited) {
return;
}
let that = this;
// before show console panel,
// trigger a transitionstart event to make panel's property 'display' change from 'none' to 'block'
let $panel = $.one('.vc-panel', this.$dom);
$panel.style.display = 'block';
// set 10ms delay to fix confict between display and transition
setTimeout(function() {
$.addClass(that.$dom, 'vc-toggle');
that._triggerPluginsEvent('showConsole');
let $mask = $.one('.vc-mask', that.$dom);
$mask.style.display = 'block';
}, 10);
}
/**
* hide console panel
* @public
*/
hide() {
if (!this.isInited) {
return;
}
$.removeClass(this.$dom, 'vc-toggle');
setTimeout(() => {
// panel will be hidden by CSS transition in 0.3s
$.one('.vc-mask', this.$dom).style.display = 'none';
$.one('.vc-panel', this.$dom).style.display = 'none';
}, 330);
this._triggerPluginsEvent('hideConsole');
}
/**
* show switch button
* @public
*/
showSwitch() {
if (!this.isInited) {
return;
}
let $switch = $.one('.vc-switch', this.$dom);
$switch.style.display = 'block';
}
/**
* hide switch button
*/
hideSwitch() {
if (!this.isInited) {
return;
}
let $switch = $.one('.vc-switch', this.$dom);
$switch.style.display = 'none';
}
/**
* show a tab
* @public
*/
showTab(tabID) {
if (!this.isInited) {
return;
}
let $logbox = $.one('#__vc_log_' + tabID);
// set actived status
$.removeClass($.all('.vc-tab', this.$dom), 'vc-actived');
$.addClass($.one('#__vc_tab_' + tabID), 'vc-actived');
$.removeClass($.all('.vc-logbox', this.$dom), 'vc-actived');
$.addClass($logbox, 'vc-actived');
// show topbar
let $curTopbar = $.all('.vc-topbar-' + tabID, this.$dom);
$.removeClass($.all('.vc-toptab', this.$dom), 'vc-toggle');
$.addClass($curTopbar, 'vc-toggle');
if ($curTopbar.length > 0) {
$.addClass($.one('.vc-content', this.$dom), 'vc-has-topbar');
} else {
$.removeClass($.one('.vc-content', this.$dom), 'vc-has-topbar');
}
// show toolbar
$.removeClass($.all('.vc-tool', this.$dom), 'vc-toggle');
$.addClass($.all('.vc-tool-' + tabID, this.$dom), 'vc-toggle');
// trigger plugin event
this.activedTab && this._triggerPluginEvent(this.activedTab, 'hide');
this.activedTab = tabID;
this._triggerPluginEvent(this.activedTab, 'show');
}
/**
* update option(s)
* @public
*/
setOption(keyOrObj, value) {
if (tool.isString(keyOrObj)) {
this.option[keyOrObj] = value;
this._triggerPluginsEvent('updateOption');
this._updateTheme();
} else if (tool.isObject(keyOrObj)) {
for (let k in keyOrObj) {
this.option[k] = keyOrObj[k];
}
this._triggerPluginsEvent('updateOption');
this._updateTheme();
} else {
console.debug('The first parameter of vConsole.setOption() must be a string or an object.');
}
}
/**
* uninstall vConsole
* @public
*/
destroy() {
if (!this.isInited) {
return;
}
// remove plugins
let IDs = Object.keys(this.pluginList);
for (let i = IDs.length - 1; i >= 0; i--) {
this.removePlugin(IDs[i]);
}
// remove DOM
this.$dom.parentNode.removeChild(this.$dom);
// reverse isInited when destroyed
this.isInited = false;
}
} // END class
// export static class
VConsole.VConsolePlugin = VConsolePlugin;
VConsole.VConsoleLogPlugin = VConsoleLogPlugin;
VConsole.VConsoleDefaultPlugin = VConsoleDefaultPlugin;
VConsole.VConsoleSystemPlugin = VConsoleSystemPlugin;
VConsole.VConsoleNetworkPlugin = VConsoleNetworkPlugin;
VConsole.VConsoleElementPlugin = VConsoleElementPlugin;
VConsole.VConsoleStoragePlugin = VConsoleStoragePlugin;
export default VConsole;
import pkg from '../../package.json';
import * as tool from '../lib/tool.js';
import $ from '../lib/query.js';
import './core.less';
import tpl from './core.html';
import tplTabbar from './tabbar.html';
import tplTabbox from './tabbox.html';
import tplTopBarItem from './topbar_item.html';
import tplToolItem from './tool_item.html';
// built-in plugins
import VConsolePlugin from '../lib/plugin.js';
import VConsoleLogPlugin from '../log/log.js';
import VConsoleDefaultPlugin from '../log/default.js';
import VConsoleSystemPlugin from '../log/system.js';
import VConsoleNetworkPlugin from '../network/network.js';
import VConsoleElementPlugin from '../element/element.js';
import VConsoleStoragePlugin from '../storage/storage.js';
const VCONSOLE_ID = '#__vconsole';
class VConsole {
constructor(opt) {
if (!!$.one(VCONSOLE_ID)) {
console.debug('vConsole is already exists.');
return;
}
let that = this;
this.version = pkg.version;
this.$dom = null;
this.isInited = false;
this.option = {
defaultPlugins: ['system', 'network', 'element', 'storage']
};
this.activedTab = '';
this.tabList = [];
this.pluginList = {};
this.switchPos = {
hasMoved: false, // exclude click event
x: 0, // right
y: 0, // bottom
startX: 0,
startY: 0,
endX: 0,
endY: 0
};
// export helper functions to public
this.tool = tool;
this.$ = $;
// merge options
if (tool.isObject(opt)) {
for (let key in opt) {
this.option[key] = opt[key];
}
}
// add built-in plugins
this._addBuiltInPlugins();
// try to init
let _onload = function() {
if (that.isInited) {
return;
}
that._render();
that._mockTap();
that._bindEvent();
that._autoRun();
};
if (document !== undefined) {
if (document.readyState === 'loading') {
$.bind(window, 'DOMContentLoaded', _onload);
} else {
_onload();
}
} else {
// if document does not exist, wait for it
let _timer;
let _pollingDocument = function() {
if (!!document && document.readyState == 'complete') {
_timer && clearTimeout(_timer);
_onload();
} else {
_timer = setTimeout(_pollingDocument, 1);
}
};
_timer = setTimeout(_pollingDocument, 1);
}
}
/**
* add built-in plugins
*/
_addBuiltInPlugins() {
// add default log plugin
this.addPlugin(new VConsoleDefaultPlugin('default', 'Log'));
// add other built-in plugins according to user's config
const list = this.option.defaultPlugins;
const plugins = {
'system': {proto: VConsoleSystemPlugin, name: 'System'},
'network': {proto: VConsoleNetworkPlugin, name: 'Network'},
'element': {proto: VConsoleElementPlugin, name: 'Element'},
'storage': {proto: VConsoleStoragePlugin, name: 'Storage'}
};
if (!!list && tool.isArray(list)) {
for (let i=0; i<list.length; i++) {
let tab = plugins[list[i]];
if (!!tab) {
this.addPlugin(new tab.proto(list[i], tab.name));
} else {
console.debug('Unrecognized default plugin ID:', list[i]);
}
}
}
}
/**
* render panel DOM
* @private
*/
_render() {
if (! $.one(VCONSOLE_ID)) {
const e = document.createElement('div');
e.innerHTML = tpl;
document.documentElement.insertAdjacentElement('beforeend', e.children[0]);
}
this.$dom = $.one(VCONSOLE_ID);
// reposition switch button
const $switch = $.one('.vc-switch', this.$dom);
let switchX = tool.getStorage('switch_x') * 1,
switchY = tool.getStorage('switch_y') * 1;
this.setSwitchPosition(switchX, switchY);
// modify font-size
const dpr = window.devicePixelRatio || 1;
const viewportEl = document.querySelector('[name="viewport"]');
if (viewportEl && viewportEl.content) {
const initialScale = viewportEl.content.match(/initial\-scale\=\d+(\.\d+)?/);
const scale = initialScale ? parseFloat(initialScale[0].split('=')[1]) : 1;
if (scale < 1) {
this.$dom.style.fontSize = 13 * dpr + 'px';
}
}
// remove from less to present transition effect
$.one('.vc-mask', this.$dom).style.display = 'none';
// set theme
this._updateTheme();
}
/**
* Update theme
* @private
*/
_updateTheme() {
const theme = this.option.theme || 'light';
this.$dom.setAttribute('data-theme', theme);
}
setSwitchPosition(switchX, switchY) {
const $switch = $.one('.vc-switch', this.$dom);
[switchX, switchY] = this._getSwitchButtonSafeAreaXY($switch, switchX, switchY);
this.switchPos.x = switchX;
this.switchPos.y = switchY;
$switch.style.right = switchX + 'px';
$switch.style.bottom = switchY + 'px';
tool.setStorage('switch_x', switchX);
tool.setStorage('switch_y', switchY);
}
/**
* Get an safe [x, y] position for switch button
* @private
*/
_getSwitchButtonSafeAreaXY($switch, x, y) {
const docWidth = Math.max(document.documentElement.offsetWidth, window.innerWidth);
const docHeight = Math.max(document.documentElement.offsetHeight, window.innerHeight);
// check edge
if (x + $switch.offsetWidth > docWidth) {
x = docWidth - $switch.offsetWidth;
}
if (y + $switch.offsetHeight > docHeight) {
y = docHeight - $switch.offsetHeight;
}
if (x < 0) { x = 0; }
if (y < 20) { y = 20; } // safe area for iOS Home indicator
return [x, y];
}
/**
* Get an safe [x, y] position for switch button
* @private
*/
_getSwitchButtonSafeAreaXY($switch, x, y) {
const docWidth = Math.max(document.documentElement.offsetWidth, window.innerWidth);
const docHeight = Math.max(document.documentElement.offsetHeight, window.innerHeight);
// check edge
if (x + $switch.offsetWidth > docWidth) {
x = docWidth - $switch.offsetWidth;
}
if (y + $switch.offsetHeight > docHeight) {
y = docHeight - $switch.offsetHeight;
}
if (x < 0) { x = 0; }
if (y < 20) { y = 20; } // safe area for iOS Home indicator
return [x, y];
}
/**
* simulate tap event by touchstart & touchend
* @private
*/
_mockTap() {
let tapTime = 700, // maximun tap interval
tapBoundary = 10; // max tap move distance
let lastTouchStartTime,
touchstartX,
touchstartY,
touchHasMoved = false,
targetElem = null;
this.$dom.addEventListener('touchstart', function(e) { // todo: if double click
if (lastTouchStartTime === undefined) {
let touch = e.targetTouches[0];
touchstartX = touch.pageX;
touchstartY = touch.pageY;
lastTouchStartTime = e.timeStamp;
targetElem = (e.target.nodeType === Node.TEXT_NODE ? e.target.parentNode : e.target);
}
}, false);
this.$dom.addEventListener('touchmove', function(e) {
let touch = e.changedTouches[0];
if (Math.abs(touch.pageX - touchstartX) > tapBoundary || Math.abs(touch.pageY - touchstartY) > tapBoundary) {
touchHasMoved = true;
}
});
this.$dom.addEventListener('touchend', function(e) {
// move and time within limits, manually trigger `click` event
if (touchHasMoved === false && e.timeStamp - lastTouchStartTime < tapTime && targetElem != null) {
let tagName = targetElem.tagName.toLowerCase(),
needFocus = false;
switch (tagName) {
case 'textarea': // focus
needFocus = true; break;
case 'input':
switch (targetElem.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
needFocus = false; break;
default:
needFocus = !targetElem.disabled && !targetElem.readOnly;
}
default:
break;
}
if (needFocus) {
targetElem.focus();
} else if (typeof window.getSelection !== 'function' || !getSelection().rangeCount) {
e.preventDefault(); // prevent click 300ms later
}
if (!targetElem.disabled && !targetElem.readOnly) {
let touch = e.changedTouches[0];
let event = document.createEvent('MouseEvents');
event.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
event.forwardedTouchEvent = true;
event.initEvent('click', true, true);
targetElem.dispatchEvent(event);
}
}
// reset values
lastTouchStartTime = undefined;
touchHasMoved = false;
targetElem = null;
}, false);
}
/**
* bind DOM events
* @private
*/
_bindEvent() {
const that = this;
// drag & drop switch button
const $switch = $.one('.vc-switch', that.$dom);
$.bind($switch, 'touchstart', function(e) {
that.switchPos.startX = e.touches[0].pageX;
that.switchPos.startY = e.touches[0].pageY;
that.switchPos.hasMoved = false;
});
$.bind($switch, 'touchend', function(e) {
if (!that.switchPos.hasMoved) {
return;
}
that.switchPos.startX = 0;
that.switchPos.startY = 0;
that.switchPos.hasMoved = false;
that.setSwitchPosition(that.switchPos.endX, that.switchPos.endY);
});
$.bind($switch, 'touchmove', function(e) {
if (e.touches.length <= 0) {
return;
}
const offsetX = e.touches[0].pageX - that.switchPos.startX,
offsetY = e.touches[0].pageY - that.switchPos.startY;
let x = Math.floor(that.switchPos.x - offsetX),
y = Math.floor(that.switchPos.y - offsetY);
[x, y] = that._getSwitchButtonSafeAreaXY($switch, x, y);
$switch.style.right = x + 'px';
$switch.style.bottom = y + 'px';
that.switchPos.endX = x;
that.switchPos.endY = y;
that.switchPos.hasMoved = true;
e.preventDefault();
});
// show console panel
$.bind($.one('.vc-switch', that.$dom), 'click', function() {
that.show();
});
// hide console panel
$.bind($.one('.vc-hide', that.$dom), 'click', function() {
that.hide();
});
// hide console panel when tap background mask
$.bind($.one('.vc-mask', that.$dom), 'click', function(e) {
if (e.target != $.one('.vc-mask')) {
return false;
}
that.hide();
});
// show tab box
$.delegate($.one('.vc-tabbar', that.$dom), 'click', '.vc-tab', function(e) {
let tabName = this.dataset.tab;
if (tabName == that.activedTab) {
return;
}
that.showTab(tabName);
});
// disable background scrolling
let $content = $.one('.vc-content', that.$dom);
let preventMove = false;
$.bind($content, 'touchstart', function (e) {
let top = $content.scrollTop,
totalScroll = $content.scrollHeight,
currentScroll = top + $content.offsetHeight;
if (top === 0) {
// when content is on the top,
// reset scrollTop to lower position to prevent iOS apply scroll action to background
$content.scrollTop = 1;
// however, when content's height is less than its container's height,
// scrollTop always equals to 0 (it is always on the top),
// so we need to prevent scroll event manually
if ($content.scrollTop === 0) {
if (!$.hasClass(e.target, 'vc-cmd-input')) { // skip input
preventMove = true;
}
}
} else if (currentScroll === totalScroll) {
// when content is on the bottom,
// do similar processing
$content.scrollTop = top - 1;
if ($content.scrollTop === top) {
if (!$.hasClass(e.target, 'vc-cmd-input')) {
preventMove = true;
}
}
}
});
$.bind($content, 'touchmove', function (e) {
if (preventMove) {
e.preventDefault();
}
});
$.bind($content, 'touchend', function (e) {
preventMove = false;
});
};
/**
* auto run after initialization
* @private
*/
_autoRun() {
this.isInited = true;
// init plugins
for (let id in this.pluginList) {
this._initPlugin(this.pluginList[id]);
}
// show first tab
if (this.tabList.length > 0) {
this.showTab(this.tabList[0]);
}
this.triggerEvent('ready');
}
/**
* trigger a vConsole.option event
* @protect
*/
triggerEvent(eventName, param) {
eventName = 'on' + eventName.charAt(0).toUpperCase() + eventName.slice(1);
if (tool.isFunction(this.option[eventName])) {
this.option[eventName].apply(this, param);
}
}
/**
* init a plugin
* @private
*/
_initPlugin(plugin) {
let that = this;
plugin.vConsole = this;
// start init
plugin.trigger('init');
// render tab (if it is a tab plugin then it should has tab-related events)
plugin.trigger('renderTab', function(tabboxHTML) {
// add to tabList
that.tabList.push(plugin.id);
// render tabbar
let $tabbar = $.render(tplTabbar, {id: plugin.id, name: plugin.name});
$.one('.vc-tabbar', that.$dom).insertAdjacentElement('beforeend', $tabbar);
// render tabbox
let $tabbox = $.render(tplTabbox, {id: plugin.id});
if (!!tabboxHTML) {
if (tool.isString(tabboxHTML)) {
$tabbox.innerHTML += tabboxHTML;
} else if (tool.isFunction(tabboxHTML.appendTo)) {
tabboxHTML.appendTo($tabbox);
} else if (tool.isElement(tabboxHTML)) {
$tabbox.insertAdjacentElement('beforeend', tabboxHTML);
}
}
$.one('.vc-content', that.$dom).insertAdjacentElement('beforeend', $tabbox);
});
// render top bar
plugin.trigger('addTopBar', function(btnList) {
if (!btnList) {
return;
}
let $topbar = $.one('.vc-topbar', that.$dom);
for (let i=0; i<btnList.length; i++) {
let item = btnList[i];
let $item = $.render(tplTopBarItem, {
name: item.name || 'Undefined',
className: item.className || '',
pluginID: plugin.id
});
if (item.data) {
for (let k in item.data) {
$item.dataset[k] = item.data[k];
}
}
if (tool.isFunction(item.onClick)) {
$.bind($item, 'click', function(e) {
let enable = item.onClick.call($item);
if (enable === false) {
// do nothing
} else {
$.removeClass($.all('.vc-topbar-' + plugin.id), 'vc-actived');
$.addClass($item, 'vc-actived');
}
});
}
$topbar.insertAdjacentElement('beforeend', $item);
}
});
// render tool bar
plugin.trigger('addTool', function(toolList) {
if (!toolList) {
return;
}
let $defaultBtn = $.one('.vc-tool-last', that.$dom);
for (let i=0; i<toolList.length; i++) {
let item = toolList[i];
let $item = $.render(tplToolItem, {
name: item.name || 'Undefined',
pluginID: plugin.id
});
if (item.global == true) {
$.addClass($item, 'vc-global-tool');
}
if (tool.isFunction(item.onClick)) {
$.bind($item, 'click', function(e) {
item.onClick.call($item);
});
}
$defaultBtn.parentNode.insertBefore($item, $defaultBtn);
}
});
// end init
plugin.isReady = true;
plugin.trigger('ready');
}
/**
* trigger an event for each plugin
* @private
*/
_triggerPluginsEvent(eventName) {
for (let id in this.pluginList) {
if (this.pluginList[id].isReady) {
this.pluginList[id].trigger(eventName);
}
}
}
/**
* trigger an event by plugin's name
* @private
*/
_triggerPluginEvent(pluginName, eventName) {
let plugin = this.pluginList[pluginName];
if (!!plugin && plugin.isReady) {
plugin.trigger(eventName);
}
}
/**
* add a new plugin
* @public
* @param object VConsolePlugin object
* @return boolean
*/
addPlugin(plugin) {
// ignore this plugin if it has already been installed
if (this.pluginList[plugin.id] !== undefined) {
console.debug('Plugin ' + plugin.id + ' has already been added.');
return false;
}
this.pluginList[plugin.id] = plugin;
// init plugin only if vConsole is ready
if (this.isInited) {
this._initPlugin(plugin);
// if it's the first plugin, show it by default
if (this.tabList.length == 1) {
this.showTab(this.tabList[0]);
}
}
return true;
}
/**
* remove a plugin
* @public
* @param string pluginID
* @return boolean
*/
removePlugin(pluginID) {
pluginID = (pluginID + '').toLowerCase();
let plugin = this.pluginList[pluginID];
// skip if is has not been installed
if (plugin === undefined) {
console.debug('Plugin ' + pluginID + ' does not exist.');
return false;
}
// trigger `remove` event before uninstall
plugin.trigger('remove');
// the plugin will not be initialized before vConsole is ready,
// so the plugin does not need to handle DOM-related actions in this case
if (this.isInited) {
let $tabbar = $.one('#__vc_tab_' + pluginID);
$tabbar && $tabbar.parentNode.removeChild($tabbar);
// remove topbar
let $topbar = $.all('.vc-topbar-' + pluginID, this.$dom);
for (let i=0; i<$topbar.length; i++) {
$topbar[i].parentNode.removeChild($topbar[i]);
}
// remove content
let $content = $.one('#__vc_log_' + pluginID);
$content && $content.parentNode.removeChild($content);
// remove tool bar
let $toolbar = $.all('.vc-tool-' + pluginID, this.$dom);
for (let i=0; i<$toolbar.length; i++) {
$toolbar[i].parentNode.removeChild($toolbar[i]);
}
}
// remove plugin from list
let index = this.tabList.indexOf(pluginID);
if (index > -1) {
this.tabList.splice(index, 1);
}
try {
delete this.pluginList[pluginID];
} catch (e) {
this.pluginList[pluginID] = undefined;
}
// show the first plugin by default
if (this.activedTab == pluginID) {
if (this.tabList.length > 0) {
this.showTab(this.tabList[0]);
}
}
return true;
}
/**
* show console panel
* @public
*/
show() {
if (!this.isInited) {
return;
}
let that = this;
// before show console panel,
// trigger a transitionstart event to make panel's property 'display' change from 'none' to 'block'
let $panel = $.one('.vc-panel', this.$dom);
$panel.style.display = 'block';
// set 10ms delay to fix confict between display and transition
setTimeout(function() {
$.addClass(that.$dom, 'vc-toggle');
that._triggerPluginsEvent('showConsole');
let $mask = $.one('.vc-mask', that.$dom);
$mask.style.display = 'block';
}, 10);
}
/**
* hide console panel
* @public
*/
hide() {
if (!this.isInited) {
return;
}
$.removeClass(this.$dom, 'vc-toggle');
setTimeout(() => {
// panel will be hidden by CSS transition in 0.3s
$.one('.vc-mask', this.$dom).style.display = 'none';
$.one('.vc-panel', this.$dom).style.display = 'none';
}, 330);
this._triggerPluginsEvent('hideConsole');
}
/**
* show switch button
* @public
*/
showSwitch() {
if (!this.isInited) {
return;
}
let $switch = $.one('.vc-switch', this.$dom);
$switch.style.display = 'block';
}
/**
* hide switch button
*/
hideSwitch() {
if (!this.isInited) {
return;
}
let $switch = $.one('.vc-switch', this.$dom);
$switch.style.display = 'none';
}
/**
* show a tab
* @public
*/
showTab(tabID) {
if (!this.isInited) {
return;
}
let $logbox = $.one('#__vc_log_' + tabID);
// set actived status
$.removeClass($.all('.vc-tab', this.$dom), 'vc-actived');
$.addClass($.one('#__vc_tab_' + tabID), 'vc-actived');
$.removeClass($.all('.vc-logbox', this.$dom), 'vc-actived');
$.addClass($logbox, 'vc-actived');
// show topbar
let $curTopbar = $.all('.vc-topbar-' + tabID, this.$dom);
$.removeClass($.all('.vc-toptab', this.$dom), 'vc-toggle');
$.addClass($curTopbar, 'vc-toggle');
if ($curTopbar.length > 0) {
$.addClass($.one('.vc-content', this.$dom), 'vc-has-topbar');
} else {
$.removeClass($.one('.vc-content', this.$dom), 'vc-has-topbar');
}
// show toolbar
$.removeClass($.all('.vc-tool', this.$dom), 'vc-toggle');
$.addClass($.all('.vc-tool-' + tabID, this.$dom), 'vc-toggle');
// trigger plugin event
this.activedTab && this._triggerPluginEvent(this.activedTab, 'hide');
this.activedTab = tabID;
this._triggerPluginEvent(this.activedTab, 'show');
}
/**
* update option(s)
* @public
*/
setOption(keyOrObj, value) {
if (tool.isString(keyOrObj)) {
this.option[keyOrObj] = value;
this._triggerPluginsEvent('updateOption');
this._updateTheme();
} else if (tool.isObject(keyOrObj)) {
for (let k in keyOrObj) {
this.option[k] = keyOrObj[k];
}
this._triggerPluginsEvent('updateOption');
this._updateTheme();
} else {
console.debug('The first parameter of vConsole.setOption() must be a string or an object.');
}
}
/**
* uninstall vConsole
* @public
*/
destroy() {
if (!this.isInited) {
return;
}
// remove plugins
let IDs = Object.keys(this.pluginList);
for (let i = IDs.length - 1; i >= 0; i--) {
this.removePlugin(IDs[i]);
}
// remove DOM
this.$dom.parentNode.removeChild(this.$dom);
// reverse isInited when destroyed
this.isInited = false;
}
} // END class
// export static class
VConsole.VConsolePlugin = VConsolePlugin;
VConsole.VConsoleLogPlugin = VConsoleLogPlugin;
VConsole.VConsoleDefaultPlugin = VConsoleDefaultPlugin;
VConsole.VConsoleSystemPlugin = VConsoleSystemPlugin;
VConsole.VConsoleNetworkPlugin = VConsoleNetworkPlugin;
VConsole.VConsoleElementPlugin = VConsoleElementPlugin;
VConsole.VConsoleStoragePlugin = VConsoleStoragePlugin;
export default VConsole;

@@ -182,2 +182,15 @@ /*

export function circularReplacer() {
const seen = [];
return (key, value) => {
if (typeof(value) === 'object' && value !== null) {
if (seen.indexOf(value) >= 0) {
return '[Circular]';
}
seen.push(value);
}
return value;
};
};
/**

@@ -202,3 +215,5 @@ * get an object's all keys ignore whether they are not enumerable

}
return keys.sort();
return keys.sort((a, b) => {
return a.localeCompare(b, undefined, { numeric: true, sensitivity: 'base' });
});
}

@@ -205,0 +220,0 @@

@@ -16,2 +16,3 @@ /*

import copy from 'copy-text-to-clipboard';
import * as tool from '../lib/tool.js';

@@ -31,2 +32,3 @@ import $ from '../lib/query.js';

};
let cachedLogs = {}; // for copy

@@ -158,2 +160,17 @@ class VConsoleLogTab extends VConsolePlugin {

that.logList = [];
// copy
$.delegate(that.$tabbox, 'click', '.vc-item-copy', (e) => {
const btn = e.target.closest('.vc-item-copy');
const { id } = btn.closest('.vc-item');
const text = cachedLogs[id];
if (text != null && copy(text)) {
btn.classList.add('vc-item-copy-success');
setTimeout(() => {
btn.classList.remove('vc-item-copy-success');
}, 600);
};
});
}

@@ -180,2 +197,3 @@

}
cachedLogs = {};
}

@@ -305,2 +323,3 @@

preLog = {};
cachedLogs = {};
}

@@ -497,23 +516,33 @@

let $content = $.one('.vc-item-content', $line);
const rawLogs = [];
// generate content from item.logs
for (let i = 0; i < logs.length; i++) {
const curLog = logs[i];
let rawLog;
let log;
try {
if (logs[i] === '') {
if (curLog === '') {
// ignore empty string
continue;
} else if (tool.isFunction(logs[i])) {
} else if (tool.isFunction(curLog)) {
// convert function to string
log = '<span> ' + logs[i].toString() + '</span>';
} else if (tool.isObject(logs[i]) || tool.isArray(logs[i])) {
rawLog = curLog.toString();
log = `<span> ${rawLog}</span>`;
} else if (tool.isObject(curLog) || tool.isArray(curLog)) {
// object or array
log = this.getFoldedLine(logs[i]);
rawLog = JSON.stringify(curLog, tool.circularReplacer(), 2)
log = this.getFoldedLine(curLog);
} else {
// default
log = (logStyle[i] ? `<span style="${logStyle[i]}"> ` : '<span> ') + tool.htmlEncode(logs[i]).replace(/\n/g, '<br/>') + '</span>';
rawLog = curLog;
log = (logStyle[i] ? `<span style="${logStyle[i]}"> ` : '<span> ') + tool.htmlEncode(curLog).replace(/\n/g, '<br/>') + '</span>';
}
} catch (e) {
log = '<span> [' + (typeof logs[i]) + ']</span>';
rawLog = typeof curLog;
log = `<span> [${rawLog}]</span>`;
}
if (log) {
rawLogs.push(rawLog);
if (typeof log === 'string')

@@ -526,2 +555,5 @@ $content.insertAdjacentHTML('beforeend', log);

// for copy
cachedLogs[item._id] = rawLogs.join(' ');
// generate content from item.content

@@ -528,0 +560,0 @@ if (tool.isObject(item.content)) {

@@ -34,43 +34,45 @@ /*

// print system info
let ua = navigator.userAgent,
logMsg = '';
const ua = navigator.userAgent;
let logMsg = [];
// wechat client version
let wxVersion = ua.match(/MicroMessenger\/([\d\.]+)/i);
wxVersion = wxVersion && wxVersion[1] ? wxVersion[1] : null;
const isMiniprogram = location.host === 'servicewechat.com';
// location
console.info('[system]', 'Location:', location.href);
if (!isMiniprogram) {
console.info('[system]', 'Location:', location.href);
}
// device & system
let ipod = ua.match(/(ipod).*\s([\d_]+)/i),
const ipod = ua.match(/(ipod).*\s([\d_]+)/i),
ipad = ua.match(/(ipad).*\s([\d_]+)/i),
iphone = ua.match(/(iphone)\sos\s([\d_]+)/i),
android = ua.match(/(android)\s([\d\.]+)/i);
android = ua.match(/(android)\s([\d\.]+)/i),
mac = ua.match(/(Mac OS X)\s([\d_]+)/i);
logMsg = 'Unknown';
logMsg = [];
if (android) {
logMsg = 'Android ' + android[2];
logMsg.push('Android ' + android[2]);
} else if (iphone) {
logMsg = 'iPhone, iOS ' + iphone[2].replace(/_/g,'.');
logMsg.push('iPhone, iOS ' + iphone[2].replace(/_/g,'.'));
} else if (ipad) {
logMsg = 'iPad, iOS ' + ipad[2].replace(/_/g, '.');
logMsg.push('iPad, iOS ' + ipad[2].replace(/_/g, '.'));
} else if (ipod) {
logMsg = 'iPod, iOS ' + ipod[2].replace(/_/g, '.');
logMsg.push('iPod, iOS ' + ipod[2].replace(/_/g, '.'));
} else if (mac) {
logMsg.push('Mac, MacOS ' + mac[2].replace(/_/g, '.'));
}
let templogMsg = logMsg;
// wechat client version
let version = ua.match(/MicroMessenger\/([\d\.]+)/i);
logMsg = 'Unknown';
if (version && version[1]) {
logMsg = version[1];
templogMsg += (', WeChat ' + logMsg);
console.info('[system]', 'System:', templogMsg);
} else {
console.info('[system]', 'System:', templogMsg);
if (wxVersion) {
logMsg.push('WeChat ' + wxVersion);
}
console.info('[system]', 'Client:', logMsg.length ? logMsg.join(', ') : 'Unknown');
// network type
let network = ua.toLowerCase().match(/ nettype\/([^ ]+)/g);
logMsg = 'Unknown';
const network = ua.toLowerCase().match(/ nettype\/([^ ]+)/g);
if (network && network[0]) {
network = network[0].split('/');
logMsg = network[1];
console.info('[system]', 'Network:', logMsg);
logMsg = [network[1]];
console.info('[system]', 'Network:', logMsg.length ? logMsg.join(', ') : 'Unknown');
}

@@ -77,0 +79,0 @@

@@ -112,3 +112,3 @@ /*

if (this.isInBottom == true) {
this.scrollToBottom();
this.autoScrollToBottom();
}

@@ -123,2 +123,8 @@ }

if (this.isInBottom == true) {
this.autoScrollToBottom();
}
}
autoScrollToBottom() {
if (!this.vConsole.option.disableLogScrolling) {
this.scrollToBottom();

@@ -259,4 +265,4 @@ }

// scroll to bottom
if (this.isInBottom) {
this.scrollToBottom();
if (this.isInBottom && this.isShow) {
this.autoScrollToBottom();
}

@@ -512,17 +518,33 @@ }

return _fetch(url, init).then((response) => {
response.clone().json().then((json) => {
item.endTime = +new Date(),
item.costTime = item.endTime - (item.startTime || item.endTime);
item.status = response.status;
item.header = {};
for (let pair of response.headers.entries()) {
item.header[pair[0]] = pair[1];
}
item.response = json;
item.readyState = 4;
const contentType = response.headers.get('content-type');
item.responseType = contentType.includes('application/json') ? 'json' : contentType.includes('text/html') ? 'text' : '';
return json;
})
that.updateRequest(id, item);
response
.clone()
.text()
.then((text) => {
const contentType = response.headers.get('content-type');
// use 'text' as default type in case of contentType is json but response is not real JSON
let itemResponse = text;
let itemResponseType = '';
if (contentType.includes('application/json')) {
try {
itemResponse = JSON.parse(text);
itemResponseType = 'json';
} catch (e) {}
} else if (contentType.includes('text/html')) {
itemResponseType = 'text';
}
item.endTime = +new Date();
item.costTime = item.endTime - (item.startTime || item.endTime);
item.status = response.status;
item.header = {};
for (let pair of response.headers.entries()) {
item.header[pair[0]] = pair[1];
}
item.response = itemResponse;
item.readyState = 4;
item.responseType = itemResponseType;
return itemResponse;
}).finally(() => {
that.updateRequest(id, item);
});
return response;

@@ -529,0 +551,0 @@ })

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc