├── .gitignore ├── LICENSE ├── README.md ├── build ├── README.md ├── build.js └── package.json ├── images ├── close.png └── open.png ├── package.json ├── src ├── css │ └── style.css ├── index.html ├── main.js └── utils │ ├── dom.js │ ├── drag.js │ ├── fetch.js │ ├── type.js │ └── xhr.js ├── tests ├── index.html └── simple.html └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | .idea 8 | build 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional REPL history 38 | .node_repl_history 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 DuJia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # a better console tool https://github.com/Tencent/vConsole 2 | 3 | # console.js 4 | 5 | [![npm version](https://img.shields.io/npm/v/mobile-console.js.svg?style=flat-square)](https://www.npmjs.com/package/mobile-console.js) 6 | [![npm downloads](https://img.shields.io/npm/dt/mobile-console.js.svg?style=flat-square)](https://www.npmjs.com/package/mobile-console.js) 7 | 8 | a console panel for mobile phone, replace alert. 9 | https://www.npmjs.com/package/mobile-console.js 10 | 11 | Preview 12 | ------- 13 | 14 | ### close status 15 |
16 | 17 |
18 | 19 | ### open status 20 |
21 | 22 |
23 | 24 | ### build 25 | 26 | ``` bash 27 | npm run build 28 | ``` 29 | 30 | ### install 31 | 32 | ``` bash 33 | npm i mobile-console.js --save-dev 34 | ``` 35 | 36 | ### ES6 37 | 38 | ``` 39 | import 'mobile-console.js'; 40 | new MobileConsole(); 41 | ``` 42 | 43 | ### demo 44 | 45 | open `tests/index.html` 46 | 47 | ### feature 48 | 49 | - support to show console.log api output. (info, warn, error, debug) 50 | - support to show js error. 51 | - support to show xhr and fetch's request and response information. (default off) 52 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # console.js 2 | 3 | a console panel for mobile phone, replace alert. 4 | https://www.npmjs.com/package/mobile-console.js 5 | 6 | Preview 7 | ------- 8 | 9 | ### close status 10 |
11 | 12 |
13 | 14 | ### open status 15 |
16 | 17 |
18 | 19 | ### Build 20 | 21 | ``` bash 22 | npm run build 23 | ``` 24 | 25 | ### Install 26 | 27 | ``` bash 28 | npm i mobile-console.js --save-dev 29 | ``` 30 | 31 | ### ES6 32 | 33 | ``` 34 | import 'mobile-console.js'; 35 | new MobileConsole(); 36 | ``` 37 | 38 | ### Demo 39 | 40 | open `tests/index.html` 41 | 42 | ### Feature 43 | 44 | - Support showing console.log api output. (info, warn, error, debug) 45 | - Support showing js error. 46 | - Support showing xhr and fetch's request and response info. (default off) 47 | -------------------------------------------------------------------------------- /build/build.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) 10 | /******/ return installedModules[moduleId].exports; 11 | 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ exports: {}, 15 | /******/ id: moduleId, 16 | /******/ loaded: false 17 | /******/ }; 18 | 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | 22 | /******/ // Flag the module as loaded 23 | /******/ module.loaded = true; 24 | 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | 29 | 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | 36 | /******/ // __webpack_public_path__ 37 | /******/ __webpack_require__.p = ""; 38 | 39 | /******/ // Load entry module and return exports 40 | /******/ return __webpack_require__(0); 41 | /******/ }) 42 | /************************************************************************/ 43 | /******/ ([ 44 | /* 0 */ 45 | /***/ (function(module, exports, __webpack_require__) { 46 | 47 | 'use strict'; 48 | 49 | Object.defineProperty(exports, "__esModule", { 50 | value: true 51 | }); 52 | 53 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 54 | 55 | __webpack_require__(1); 56 | 57 | var _index = __webpack_require__(5); 58 | 59 | var _index2 = _interopRequireDefault(_index); 60 | 61 | var _dom = __webpack_require__(6); 62 | 63 | var _dom2 = _interopRequireDefault(_dom); 64 | 65 | var _drag = __webpack_require__(7); 66 | 67 | var _drag2 = _interopRequireDefault(_drag); 68 | 69 | var _xhr = __webpack_require__(8); 70 | 71 | var _xhr2 = _interopRequireDefault(_xhr); 72 | 73 | var _fetch = __webpack_require__(9); 74 | 75 | var _fetch2 = _interopRequireDefault(_fetch); 76 | 77 | var _type = __webpack_require__(10); 78 | 79 | var _type2 = _interopRequireDefault(_type); 80 | 81 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 82 | 83 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 84 | 85 | var logBoxSelector = '.-c-content'; 86 | var switchBtnSelector = '.-c-switch'; 87 | var toolBarSelector = '.-c-toolbar'; 88 | var clearClass = '-c-clear'; 89 | var hideClass = '-c-hide'; 90 | var ajaxClass = '-c-ajax'; 91 | var logItemClass = '-c-log'; 92 | var consoleMethods = ['debug', 'error', 'info', 'log', 'warn']; 93 | 94 | var Console = function () { 95 | function Console() { 96 | _classCallCheck(this, Console); 97 | 98 | this.render(); 99 | this.prepareProperty(); 100 | this.bindEvent(); 101 | this.catchAjax(); 102 | this.core(); 103 | } 104 | 105 | _createClass(Console, [{ 106 | key: 'render', 107 | value: function render() { 108 | var ele = _dom2.default.createElement('div', null, _index2.default); 109 | _dom2.default.append(_dom2.default.$('body'), ele); 110 | } 111 | }, { 112 | key: 'prepareProperty', 113 | value: function prepareProperty() { 114 | this.ajaxEnable = false; 115 | this.switchBtn = _dom2.default.$(switchBtnSelector); 116 | // 设置 Switch Button 初始位置, 并使其可以 Drag 117 | this.switchBtn.style.left = document.documentElement.clientWidth - this.switchBtn.offsetWidth - 10 + "px"; 118 | this.switchBtn.style.top = document.documentElement.clientHeight - this.switchBtn.offsetHeight - 10 + "px"; 119 | (0, _drag2.default)(this.switchBtn); 120 | this.toolBar = _dom2.default.$(toolBarSelector); 121 | this.logBox = _dom2.default.$(logBoxSelector); 122 | } 123 | }, { 124 | key: 'bindEvent', 125 | value: function bindEvent() { 126 | var _this2 = this; 127 | 128 | this.toolBar.addEventListener('click', function (e) { 129 | var target = e.target; 130 | if (target.classList.contains(clearClass)) { 131 | _dom2.default.html(_this2.logBox, ''); 132 | } else if (target.classList.contains(hideClass)) { 133 | _dom2.default.hide(_this2.logBox, _this2.toolBar).show(_this2.switchBtn); 134 | } 135 | if (target.classList.contains(ajaxClass)) { 136 | _this2.ajaxEnable = !_this2.ajaxEnable; 137 | if (_this2.ajaxEnable) { 138 | target.innerText = 'AJAX(ON)'; 139 | } else { 140 | target.innerText = 'AJAX(OFF)'; 141 | } 142 | } 143 | }); 144 | 145 | this.switchBtn.addEventListener('click', function () { 146 | _dom2.default.hide(_this2.switchBtn).show(_this2.logBox, _this2.toolBar); 147 | }); 148 | 149 | // 捕获页面错误 150 | var _onerror = window.onerror || function noop() {}; 151 | 152 | window.onerror = function (msg, url, lineNo, columnNo, error) { 153 | _onerror(); 154 | var message = ['Message: ' + msg, 'URL: ' + url, 'Line: ' + lineNo, 'Column: ' + columnNo, 'Error object: ' + error].join('
'); 155 | 156 | _this2.pushLog([message], 'Exception'); 157 | }; 158 | } 159 | }, { 160 | key: 'catchAjax', 161 | value: function catchAjax() { 162 | // 捕获 xhr 错误 163 | // TODO 添加 REQUEST BODY 和 RESPONSE DATA 164 | var _this = this; 165 | _xhr2.default.fn = function (xhr) { 166 | if (!_this.ajaxEnable) return; 167 | if (xhr.readyState === XMLHttpRequest.DONE) { 168 | if (xhr.status >= 200 && xhr.status <= 299) { 169 | _this.pushLog(['[AJAX] ' + xhr.open_fn_parmas.method + ' ' + xhr.open_fn_parmas.url + ' ' + xhr.status + ' (' + xhr.statusText + ')'], 'AJAXSUCCESS'); 170 | xhr.send_fn_params.data && _this.pushLog(['[REQUEST BODY] ' + xhr.send_fn_params.data], 'AJAXSUCCESS'); 171 | xhr.responseText && _this.pushLog(['[RESPONSE DATA] ' + xhr.responseText], 'AJAXSUCCESS'); 172 | } else { 173 | _this.pushLog(['[AJAX] ' + xhr.open_fn_parmas.method + ' ' + xhr.open_fn_parmas.url + ' ' + xhr.status + ' (' + xhr.statusText + ')'], 'AJAXFAILURE'); 174 | xhr.send_fn_params.data && _this.pushLog(['[REQUEST BODY] ' + xhr.send_fn_params.data], 'AJAXFAILURE'); 175 | xhr.responseText && _this.pushLog(['[RESPONSE DATA] ' + xhr.responseText], 'AJAXFAILURE'); 176 | } 177 | } 178 | }; 179 | 180 | window.XMLHttpRequest = _xhr2.default; 181 | // 捕获 fetch 错误 182 | var unregister = _fetch2.default.register({ 183 | response: function response(_ref) { 184 | var request = _ref.request, 185 | _response = _ref.response; 186 | 187 | if (_this.ajaxEnable) { 188 | if (_response.status >= 200 && _response.status <= 299) { 189 | _this.pushFetchLog(request, _response, 'AJAXSUCCESS'); 190 | } else { 191 | _this.pushFetchLog(request, _response, 'AJAXFAILURE'); 192 | } 193 | } 194 | return _response; 195 | }, 196 | responseError: function responseError(_ref2) { 197 | var request = _ref2.request, 198 | _responseError = _ref2.responseError; 199 | 200 | // TODO 待确定 201 | if (_this.ajaxEnable) { 202 | _this.pushLog(['[AJAX] ' + request.method + ' ' + request.url + ' ' + _responseError.status + ' (' + _responseError.statusText + ')'], 'AJAXFAILURE'); 203 | } 204 | return Promise.reject(_responseError); 205 | } 206 | }); 207 | } 208 | }, { 209 | key: 'pushFetchLog', 210 | value: function pushFetchLog(request, response, type) { 211 | var _this3 = this; 212 | 213 | try { 214 | request = request.clone(); 215 | response = response.clone(); 216 | Promise.all([request.text(), response.text()]).then(function (data) { 217 | _this3.pushLog(['[AJAX] ' + request.method + ' ' + request.url + ' ' + response.status + ' (' + response.statusText + ')'], type); 218 | data[0] && _this3.pushLog(['[REQUEST BODY] ' + data[0]], type); 219 | data[1] && _this3.pushLog(['[RESPONSE DATA] ' + data[1]], type); 220 | }).catch(function (err) { 221 | _this3.pushLog(['[AJAX] ' + request.method + ' ' + request.url + ' ' + response.status + ' (' + response.statusText + ')'], type); 222 | }); 223 | } catch (err) { 224 | this.pushLog(['[AJAX] ' + request.method + ' ' + request.url + ' ' + response.status + ' (' + response.statusText + ')'], type); 225 | } 226 | } 227 | }, { 228 | key: 'pushLog', 229 | value: function pushLog(msg, type) { 230 | var text = msg.map(function (val) { 231 | return (0, _type2.default)(val) ? '' + val.stack : JSON.stringify(val); 232 | }).join(' '), 233 | log = _dom2.default.createElement('div', { class: logItemClass + ' ' + type }, text); 234 | _dom2.default.append(this.logBox, log); 235 | this.logBox.scrollTop = this.logBox.scrollHeight; 236 | } 237 | }, { 238 | key: 'core', 239 | value: function core() { 240 | var _this4 = this; 241 | 242 | consoleMethods.forEach(function (method) { 243 | var original = window.console[method]; 244 | window.console[method] = function () { 245 | for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 246 | args[_key] = arguments[_key]; 247 | } 248 | 249 | _this4.pushLog(args, method); 250 | original.apply(console, args); 251 | }; 252 | }); 253 | } 254 | }]); 255 | 256 | return Console; 257 | }(); 258 | 259 | exports.default = Console; 260 | 261 | 262 | window.MobileConsole = Console; 263 | 264 | /***/ }), 265 | /* 1 */ 266 | /***/ (function(module, exports, __webpack_require__) { 267 | 268 | // style-loader: Adds some css to the DOM by adding a --> 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 166 | 167 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: './src/main.js', 5 | output: { 6 | path: './build', 7 | filename: 'build.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | { 12 | test: /\.html$/, 13 | loader: "html" 14 | }, 15 | { 16 | test: /\.js$/, 17 | loader: 'babel', 18 | query: { 19 | presets: ['es2015'] 20 | } 21 | }, 22 | { 23 | test: /\.css$/, 24 | loader: 'style!css' 25 | } 26 | ] 27 | } 28 | } 29 | --------------------------------------------------------------------------------