|
@@ -0,0 +1,750 @@
|
|
|
|
|
+/*
|
|
|
|
|
+Tencent is pleased to support the open source community by making vConsole available.
|
|
|
|
|
+
|
|
|
|
|
+Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
|
|
|
|
|
+
|
|
|
|
|
+Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
|
|
|
|
+http://opensource.org/licenses/MIT
|
|
|
|
|
+
|
|
|
|
|
+Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
|
|
|
+*/
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * vConsole core class
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+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 = {
|
|
|
|
|
+ x: 10, // right
|
|
|
|
|
+ y: 10, // 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)) {
|
|
|
|
|
+ let e = document.createElement('div');
|
|
|
|
|
+ e.innerHTML = tpl;
|
|
|
|
|
+ document.documentElement.insertAdjacentElement('beforeend', e.children[0]);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$dom = $.one(VCONSOLE_ID);
|
|
|
|
|
+
|
|
|
|
|
+ // reposition switch button
|
|
|
|
|
+ let $switch = $.one('.vc-switch', this.$dom);
|
|
|
|
|
+ let switchX = tool.getStorage('switch_x') * 1,
|
|
|
|
|
+ switchY = tool.getStorage('switch_y') * 1;
|
|
|
|
|
+ if (switchX || switchY) {
|
|
|
|
|
+ // check edge
|
|
|
|
|
+ if (switchX + $switch.offsetWidth > document.documentElement.offsetWidth) {
|
|
|
|
|
+ switchX = document.documentElement.offsetWidth - $switch.offsetWidth;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (switchY + $switch.offsetHeight > document.documentElement.offsetHeight) {
|
|
|
|
|
+ switchY = document.documentElement.offsetHeight - $switch.offsetHeight;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (switchX < 0) { switchX = 0; }
|
|
|
|
|
+ if (switchY < 0) { switchY = 0; }
|
|
|
|
|
+ this.switchPos.x = switchX;
|
|
|
|
|
+ this.switchPos.y = switchY;
|
|
|
|
|
+ $.one('.vc-switch').style.right = switchX + 'px';
|
|
|
|
|
+ $.one('.vc-switch').style.bottom = switchY + 'px';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // modify font-size
|
|
|
|
|
+ let dpr = window.devicePixelRatio || 1;
|
|
|
|
|
+ let viewportEl = document.querySelector('[name="viewport"]');
|
|
|
|
|
+ if (viewportEl && viewportEl.content) {
|
|
|
|
|
+ let initialScale = viewportEl.content.match(/initial\-scale\=\d+(\.\d+)?/);
|
|
|
|
|
+ let 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';
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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 {
|
|
|
|
|
+ e.preventDefault(); // prevent click 300ms later
|
|
|
|
|
+ }
|
|
|
|
|
+ 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() {
|
|
|
|
|
+ let that = this;
|
|
|
|
|
+
|
|
|
|
|
+ // drag & drop switch button
|
|
|
|
|
+ let $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;
|
|
|
|
|
+ });
|
|
|
|
|
+ $.bind($switch, 'touchend', function(e) {
|
|
|
|
|
+ that.switchPos.x = that.switchPos.endX;
|
|
|
|
|
+ that.switchPos.y = that.switchPos.endY;
|
|
|
|
|
+ that.switchPos.startX = 0;
|
|
|
|
|
+ that.switchPos.startY = 0;
|
|
|
|
|
+ tool.setStorage('switch_x', that.switchPos.x);
|
|
|
|
|
+ tool.setStorage('switch_y', that.switchPos.y);
|
|
|
|
|
+ });
|
|
|
|
|
+ $.bind($switch, 'touchmove', function(e) {
|
|
|
|
|
+ if (e.touches.length > 0) {
|
|
|
|
|
+ let offsetX = e.touches[0].pageX - that.switchPos.startX,
|
|
|
|
|
+ offsetY = e.touches[0].pageY - that.switchPos.startY;
|
|
|
|
|
+ let x = that.switchPos.x - offsetX,
|
|
|
|
|
+ y = that.switchPos.y - offsetY;
|
|
|
|
|
+ // check edge
|
|
|
|
|
+ if (x + $switch.offsetWidth > document.documentElement.offsetWidth) {
|
|
|
|
|
+ x = document.documentElement.offsetWidth - $switch.offsetWidth;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (y + $switch.offsetHeight > document.documentElement.offsetHeight) {
|
|
|
|
|
+ y = document.documentElement.offsetHeight - $switch.offsetHeight;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (x < 0) { x = 0; }
|
|
|
|
|
+ if (y < 0) { y = 0; }
|
|
|
|
|
+ $switch.style.right = x + 'px';
|
|
|
|
|
+ $switch.style.bottom = y + 'px';
|
|
|
|
|
+ that.switchPos.endX = x;
|
|
|
|
|
+ that.switchPos.endY = y;
|
|
|
|
|
+ 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);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // after console panel, trigger a transitionend event to make panel's property 'display' change from 'block' to 'none'
|
|
|
|
|
+ $.bind($.one('.vc-panel', that.$dom), 'transitionend webkitTransitionEnd oTransitionEnd otransitionend', function(e) {
|
|
|
|
|
+ if (e.target != $.one('.vc-panel')) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!$.hasClass(that.$dom, 'vc-toggle')) {
|
|
|
|
|
+ e.target.style.display = 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // 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');
|
|
|
|
|
+ this._triggerPluginsEvent('hideConsole');
|
|
|
|
|
+
|
|
|
|
|
+ let $mask = $.one('.vc-mask', this.$dom),
|
|
|
|
|
+ $panel = $.one('.vc-panel', this.$dom);
|
|
|
|
|
+ $.bind($mask, 'transitionend', function(evnet) {
|
|
|
|
|
+ $mask.style.display = 'none';
|
|
|
|
|
+ $panel.style.display = 'none';
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 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');
|
|
|
|
|
+ } else if (tool.isObject(keyOrObj)) {
|
|
|
|
|
+ for (let k in keyOrObj) {
|
|
|
|
|
+ this.option[k] = keyOrObj[k];
|
|
|
|
|
+ }
|
|
|
|
|
+ this._triggerPluginsEvent('updateOption');
|
|
|
|
|
+ } 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;
|