├── test
├── index.js
└── unit
│ ├── ajax.spec.js
│ ├── error.spec.js
│ ├── config.spec.js
│ └── index.spec.js
├── MonitorFE.gif
├── .gitignore
├── dist
├── js
│ ├── monitor.0.0.1.js.gz
│ ├── monitor.0.0.3.js.gz
│ ├── monitor.0.0.1-beta.js.gz
│ ├── monitor.0.0.2-beta.js.gz
│ ├── monitor.0.0.2-beta.js
│ ├── monitor.0.0.1.js
│ └── monitor.0.0.1-beta.js
├── package.json
├── index.html
└── README.md
├── .gitattributes
├── coverage
├── lcov-report
│ ├── sort-arrow-sprite.png
│ ├── prettify.css
│ ├── block-navigation.js
│ ├── error.js.html
│ ├── stateChange.js.html
│ ├── config.js.html
│ ├── index.js.html
│ ├── sorter.js
│ ├── base.css
│ ├── index.html
│ ├── ajax.js.html
│ └── prettify.js
├── lcov.info
├── clover.xml
└── coverage-final.json
├── .github
└── ISSUE_TEMPLATE
│ ├── custom.md
│ ├── feature_request.md
│ └── bug_report.md
├── .babelrc
├── index.js
├── version.json
├── src
├── index.js
├── config.js
├── monitor.js
├── eventCenter.js
├── vue
│ └── index.js
├── ajax.js
├── error.js
├── wrap.js
└── initMonitor.js
├── write.js
├── server
├── database
│ └── init.js
├── server.js
└── index.js
├── .eslintrc.js
├── git-hooks
└── verify-commit-msg.js
├── config
├── webpack.pro.config.js
└── webpack.dev.config.js
├── LICENSE
├── package.json
├── index.html
├── README.md
├── docs
├── README_ZH.md
└── README_EN.md
├── CODE_OF_CONDUCT.md
├── createError.js
└── CHANGELOG.md
/test/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/unit/ajax.spec.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/unit/error.spec.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test/unit/config.spec.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/MonitorFE.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MriLiuJY/monitorFE/HEAD/MonitorFE.gif
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log*
3 | .DS_Store
4 | .idea
5 | .sass-cache
6 | temp-*.html
7 | .vs
--------------------------------------------------------------------------------
/dist/js/monitor.0.0.1.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MriLiuJY/monitorFE/HEAD/dist/js/monitor.0.0.1.js.gz
--------------------------------------------------------------------------------
/dist/js/monitor.0.0.3.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MriLiuJY/monitorFE/HEAD/dist/js/monitor.0.0.3.js.gz
--------------------------------------------------------------------------------
/dist/js/monitor.0.0.1-beta.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MriLiuJY/monitorFE/HEAD/dist/js/monitor.0.0.1-beta.js.gz
--------------------------------------------------------------------------------
/dist/js/monitor.0.0.2-beta.js.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MriLiuJY/monitorFE/HEAD/dist/js/monitor.0.0.2-beta.js.gz
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.js linguist-language=JavaScript
2 | *.css linguist-language=JavaScript
3 | *.html linguist-language=JavaScript
4 |
--------------------------------------------------------------------------------
/coverage/lcov-report/sort-arrow-sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MriLiuJY/monitorFE/HEAD/coverage/lcov-report/sort-arrow-sprite.png
--------------------------------------------------------------------------------
/test/unit/index.spec.js:
--------------------------------------------------------------------------------
1 | const initMonitor = require('../../src/index');
2 |
3 | test('test initMonitor', () => {
4 | expect(initMonitor(1)).toBe(2);
5 | });
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "@babel/preset-env",
5 | {
6 | "targets": {
7 | "node": "current",
8 | },
9 | },
10 | ],
11 | ],
12 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file main js at project
3 | * @author
4 | * @version 0.0.1-beta
5 | */
6 |
7 | import Index from "./src/index";
8 |
9 | function Shell(window) {
10 | if (!window.initMonitor) {
11 | window.initMonitor = Index;
12 | }
13 | }
14 |
15 | Shell(window);
16 |
--------------------------------------------------------------------------------
/version.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fe-monitor-sdk",
3 | "version": "0.0.3",
4 | "description": "monitor front-end pro",
5 | "main": "dist/monitor.0.0.3",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "javascript",
11 | "monitor"
12 | ],
13 | "author": "jykid",
14 | "license": "MIT"
15 | }
16 |
--------------------------------------------------------------------------------
/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fe-monitor-sdk",
3 | "version": "0.0.3",
4 | "description": "monitor front-end pro",
5 | "main": "dist/monitor.0.0.3",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [
10 | "javascript",
11 | "monitor"
12 | ],
13 | "author": "jykid",
14 | "license": "MIT"
15 | }
16 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file main js at project
3 | * @author JYkid
4 | * @version 0.0.1-beta
5 | */
6 |
7 | /* eslint-disable */
8 | import InitMonitor from "./initMonitor";
9 | import Monitor from "./monitor";
10 |
11 | function Index(userConfig, callback) {
12 | const init = new InitMonitor(userConfig);
13 |
14 | callback(new Monitor(init));
15 | // 初始化
16 | return init;
17 | }
18 |
19 | module.exports = Index;
20 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file config detail file
3 | * @author JYkid
4 | * @version 0.0.1-beta
5 | */
6 |
7 | function Config(conf) {
8 | const self = this;
9 | self._extend(self, conf);
10 | }
11 |
12 | Config.prototype = {
13 | https: true,
14 | post: true,
15 | url: "/monitor",
16 | record: false,
17 | _extend: (self, conf) => {
18 | Object.keys(conf).map((x) => {
19 | self[x] = conf[x];
20 | return;
21 | });
22 | return self;
23 | },
24 | };
25 |
26 | module.exports = Config;
27 |
--------------------------------------------------------------------------------
/coverage/lcov-report/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
2 |
--------------------------------------------------------------------------------
/write.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * 该文件的目的是为了在执行npm run build 之后自动生成README.md 和package.json
4 | *
5 | *
6 | */
7 |
8 | const fs = require("fs");
9 | const path = require("path");
10 |
11 | const package = fs.readFileSync('./version.json');
12 | const readme = fs.readFileSync('./README.md');
13 |
14 | var packagePath = path.join(__dirname, './dist/package.json');
15 | var readmePath = path.join(__dirname, './dist/README.md');
16 |
17 | fs.writeFile(packagePath, package, function(err) {
18 | if (err) {
19 | return console.log(err);
20 | }
21 | });
22 |
23 | fs.writeFile(readmePath, readme, function(err) {
24 | if (err) {
25 | return console.log(err);
26 | }
27 | });
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/server/database/init.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file user account connect DB init js
3 | * @author JYkid
4 | * @version 0.0.1
5 | */
6 |
7 | const mongoose = require("mongoose");
8 | const db = "mongodb://localhost/test";
9 |
10 | mongoose.Promise = global.Promise;
11 |
12 | exports.connect = () => {
13 | if (process.env.NODE_ENV !== 'production') {
14 | mongoose.set('debug', true);
15 | }
16 |
17 | mongoose.connect(db);
18 |
19 | mongoose.connection.on("disonnected", () => {
20 | mongoose.connect(db);
21 | });
22 |
23 | mongoose.connection.on("err", err => {
24 | mongoose.connect(db);
25 | });
26 |
27 | mongoose.connection.on("open", () => {
28 | console.log("Mongoodb connect done!");
29 | });
30 | };
31 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "commonjs": true,
5 | "es6": true
6 | },
7 | "extends": "airbnb-base",
8 | "rules": {
9 | "eol-last": 2,
10 | "indent": [2],
11 | "linebreak-style": [
12 | "error",
13 | ],
14 | "quotes": [
15 | "error",
16 | "double"
17 | ],
18 | "semi": [
19 | "error",
20 | "always"
21 | ],
22 | "no-underscore-dangle": 0,
23 | "no-unused-expressions": 0,
24 | "no-useless-return": 0,
25 | "no-param-reassign": 0,
26 | "consistent-return": 0,
27 | "no-bitwise": 0,
28 | "prefer-destructuring": 0,
29 | "func-names": 0,
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/src/monitor.js:
--------------------------------------------------------------------------------
1 |
2 | /* eslint-disable */
3 |
4 | function Monitor(InitMonitor) {
5 | const self = this;
6 | self._InitMonitor = InitMonitor;
7 | return self;
8 | }
9 |
10 | Monitor.prototype = {
11 | _destory() {
12 | const self = this;
13 | const array = self._InitMonitor._getEvent();
14 | for (let i = 0; i < array.length; i++) {
15 | // event type add different stage
16 | if (array[i].type === 'error') {
17 | window.removeEventListener(array[i].type, array[i].func, true);
18 | } else {
19 | window.removeEventListener(array[i].type, array[i].func);
20 | }
21 | }
22 | },
23 | _getRecord() {
24 | const self = this;
25 | const array = self._InitMonitor._getRrwebEvent();
26 | return array
27 | },
28 | }
29 |
30 | module.exports = Monitor;
--------------------------------------------------------------------------------
/git-hooks/verify-commit-msg.js:
--------------------------------------------------------------------------------
1 | const chalk = require('chalk');
2 |
3 | const msgPath = process.env.GIT_PARAMS;
4 | const msg = require('fs').readFileSync(msgPath, 'utf-8').trim();
5 |
6 | const commitRE = /^(revert: )?(feat|fix|merge|package|docs|style|refactor|perf|test|workflow|ci|chore|types|build)(\(.+\))?: .{1,50}/;
7 |
8 | if (!commitRE.test(msg)) {
9 | console.error(
10 | ` ${chalk.bgRed.white(' ERROR ')} ${chalk.red('invalid commit message format.')}\n\n${
11 | chalk.red(' Proper commit message format is required for automated changelog generation. Examples:\n\n')
12 | } ${chalk.green('feat(compiler): add \'comments\' option')}\n`
13 | + ` ${chalk.green('fix(v-model): handle events on blur (close #28)')}\n\n${
14 | chalk.red(` You can also use ${chalk.cyan('npm run commit')} to interactively generate a commit message.\n`)}`,
15 | );
16 | process.exit(1);
17 | }
18 |
--------------------------------------------------------------------------------
/config/webpack.pro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file
3 | * @author
4 | * @version 0.1.0
5 | */
6 | const path = require("path");
7 | const webpack = require("webpack");
8 | const metadata = require("../version.json");
9 | // const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
10 | const CompressionWebpackPlugin = require('compression-webpack-plugin');
11 |
12 | module.exports = (env) => {
13 | return {
14 | mode: "production",
15 | entry: "./index.js",
16 | output: {
17 | publicPath: "/dist/js/",
18 | filename: `monitor.${metadata.version}.js`,
19 | path: path.resolve(__dirname, "../dist/js/"),
20 | },
21 | plugins: [
22 | new webpack.HashedModuleIdsPlugin(),
23 | new CompressionWebpackPlugin(),
24 | ],
25 | module: {
26 | rules: [
27 | {
28 | test: /\.js$/,
29 | exclude: /node_modules/,
30 | use: "babel-loader",
31 | },
32 | ],
33 | },
34 | };
35 | }
36 |
--------------------------------------------------------------------------------
/src/eventCenter.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file event center && record center
3 | * @since 0.0.2
4 | * @author JYkid
5 | * @version 0.0.2
6 | */
7 |
8 | function EventCenter() {
9 | const self = this;
10 | const data = [];
11 | const record = [];
12 | self.data = data;
13 | self.record = record;
14 | return self;
15 | }
16 |
17 | EventCenter.prototype = {
18 | _get() {
19 | const self = this;
20 | return self.data;
21 | },
22 | _getRecord() {
23 | const self = this;
24 | return self.record;
25 | },
26 | _set(event) {
27 | const self = this;
28 | self.data.push(event);
29 | },
30 | /**
31 | * push record event data in Array
32 | * @param { Object } event
33 | */
34 | _setRecord(event) {
35 | const self = this;
36 | self.record.push(event);
37 | },
38 | /**
39 | * clear event center data
40 | */
41 | _clearRecord() {
42 | const self = this;
43 | self.record.splice(0, self.record.length);
44 | },
45 | };
46 |
47 | module.exports = EventCenter;
48 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const express = require("express");
3 | const bodyParser = require("body-parser");
4 | const app = express();
5 |
6 | app.use(bodyParser.json());
7 | app.use(bodyParser.urlencoded({extended: false}));
8 | app.use(express.static("./"));
9 | app.use(express.static("dist"));
10 |
11 | app.use("/index", (req, res) => {
12 | const filename = "./dist/index.html";
13 | fs.readFile(filename, (err, result) => {
14 | res.set("content-type", "text/html");
15 | res.send(result);
16 | res.end();
17 | });
18 | });
19 |
20 | // create 500 in server
21 | app.use("/servererr", (req, res) => {
22 | reqq;
23 | res.send({
24 | status: "success",
25 | });
26 | });
27 |
28 | // timeout no res
29 | app.use("/timeout", (req, res) => {});
30 |
31 |
32 | // timeout no res
33 | app.use("/monitor", (req, res) => {
34 | res.send({});
35 | });
36 |
37 |
38 | const point = 9999;
39 | console.log("打包测试地址: " + "http://localhost:" +point + "/index");
40 | app.listen(point);
41 |
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 MriLiuJY
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 |
--------------------------------------------------------------------------------
/src/vue/index.js:
--------------------------------------------------------------------------------
1 | function formatComponentName(vm) {
2 | try {
3 | if (vm.$root === vm) return "root";
4 |
5 | var name = vm._isVue
6 | ? (vm.$options && vm.$options.name) ||
7 | (vm.$options && vm.$options._componentTag)
8 | : vm.name;
9 | return (
10 | (name ? "component <" + name + ">" : "anonymous component") +
11 | (vm._isVue && vm.$options && vm.$options.__file
12 | ? " at " + (vm.$options && vm.$options.__file)
13 | : "")
14 | );
15 | } catch (error) {
16 | // 无需出错处理
17 | }
18 | }
19 |
20 | export default function(monitor, Vue) {
21 | Vue.config.errorHandler = function(err, vm, info) {
22 | try {
23 | if (vm) {
24 | var componentName = formatComponentName(vm);
25 | var propsData = vm.$options && vm.$options.propsData;
26 | monitor.getJsError(err, {
27 | metaData: {
28 | componentName: componentName,
29 | propsData: propsData,
30 | info: info
31 | }
32 | });
33 | } else {
34 | monitor.notifyError(err);
35 | }
36 | } catch (error) {
37 | // 无需出错处理
38 | }
39 | };
40 | }
--------------------------------------------------------------------------------
/src/ajax.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file ajax request file
3 | * @author JYkid
4 | * @version 0.0.1-beta
5 | */
6 |
7 |
8 | /* eslint-disable */
9 | const ajax = (function() {
10 | return {
11 | canAjax: function() {
12 | return (window.XMLHttpRequest && window.JSON);
13 | },
14 | post: function(url, data, timeout) {
15 | var xhr = new XMLHttpRequest();
16 | xhr.open("post", url, true);
17 | xhr.setRequestHeader("content-type", "application/json;charset=utf-8");
18 | xhr.setRequestHeader("Accept", "application/json");
19 | xhr.withCredentials = true;
20 | xhr.timeout = timeout || 30000;
21 | xhr.onload = function () {
22 | var result = window.JSON.parse(xhr.responseText);
23 | if (result.status === 1) {
24 | changeStatus()
25 | }
26 | };
27 | xhr.onreadystatechange = function () {
28 | if (xhr.readyState === 4) {
29 | if (xhr.status === 200) {
30 | var result = window.JSON.parse(xhr.responseText);
31 | if (result.status === 1) {
32 | changeStatus()
33 | }
34 | } else {
35 | throw new Error("网络请求错误,请稍后再试~");
36 | }
37 | }
38 | };
39 | xhr.send(window.JSON.stringify(data));
40 | },
41 | getWraper: function(err, Wrap, resource) {
42 | let wrap = new Wrap();
43 | let data = wrap._geWrap();
44 |
45 | },
46 | }
47 | })();
48 |
49 | module.exports = {
50 | ajax,
51 | }
52 |
--------------------------------------------------------------------------------
/coverage/lcov.info:
--------------------------------------------------------------------------------
1 | TN:
2 | SF:/Users/jyliu/mycode/FE-Monitor/src/ajax.js
3 | FN:7,(anonymous_0)
4 | FN:9,(anonymous_1)
5 | FN:12,(anonymous_2)
6 | FN:19,(anonymous_3)
7 | FN:25,(anonymous_4)
8 | FNF:5
9 | FNH:0
10 | FNDA:0,(anonymous_0)
11 | FNDA:0,(anonymous_1)
12 | FNDA:0,(anonymous_2)
13 | FNDA:0,(anonymous_3)
14 | FNDA:0,(anonymous_4)
15 | DA:7,0
16 | DA:8,0
17 | DA:10,0
18 | DA:13,0
19 | DA:14,0
20 | DA:15,0
21 | DA:16,0
22 | DA:17,0
23 | DA:18,0
24 | DA:19,0
25 | DA:20,0
26 | DA:21,0
27 | DA:22,0
28 | DA:25,0
29 | DA:26,0
30 | DA:27,0
31 | DA:28,0
32 | DA:29,0
33 | DA:30,0
34 | DA:33,0
35 | DA:37,0
36 | DA:42,0
37 | LF:22
38 | LH:0
39 | BRDA:10,0,0,0
40 | BRDA:10,0,1,0
41 | BRDA:18,1,0,0
42 | BRDA:18,1,1,0
43 | BRDA:21,2,0,0
44 | BRDA:21,2,1,0
45 | BRDA:26,3,0,0
46 | BRDA:26,3,1,0
47 | BRDA:27,4,0,0
48 | BRDA:27,4,1,0
49 | BRDA:29,5,0,0
50 | BRDA:29,5,1,0
51 | BRF:12
52 | BRH:0
53 | end_of_record
54 | TN:
55 | SF:/Users/jyliu/mycode/FE-Monitor/src/config.js
56 | FN:7,Config
57 | FNF:1
58 | FNH:0
59 | FNDA:0,Config
60 | LF:0
61 | LH:0
62 | BRF:0
63 | BRH:0
64 | end_of_record
65 | TN:
66 | SF:/Users/jyliu/mycode/FE-Monitor/src/error.js
67 | FNF:0
68 | FNH:0
69 | LF:0
70 | LH:0
71 | BRF:0
72 | BRH:0
73 | end_of_record
74 | TN:
75 | SF:/Users/jyliu/mycode/FE-Monitor/src/index.js
76 | FN:9,initMonitor
77 | FNF:1
78 | FNH:1
79 | FNDA:1,initMonitor
80 | DA:10,1
81 | DA:11,1
82 | DA:14,1
83 | DA:16,1
84 | LF:4
85 | LH:4
86 | BRF:0
87 | BRH:0
88 | end_of_record
89 | TN:
90 | SF:/Users/jyliu/mycode/FE-Monitor/src/stateChange.js
91 | FNF:0
92 | FNH:0
93 | LF:0
94 | LH:0
95 | BRF:0
96 | BRH:0
97 | end_of_record
98 |
--------------------------------------------------------------------------------
/config/webpack.dev.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const webpack = require("webpack");
3 | const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
4 | const CompressionWebpackPlugin = require('compression-webpack-plugin');
5 |
6 | module.exports = {
7 | mode: "development",
8 | entry: "./index.js",
9 | output: {
10 | publicPath: "/dist/js/",
11 | filename: "monitor.js",
12 | path: path.resolve(__dirname, "../dist"),
13 | },
14 | devServer: {
15 | contentBase: "./dist/",
16 | port: 8080,
17 | hot: true,
18 | host: "localhost",
19 | },
20 | module: {
21 | rules: [{
22 | enforce: "pre",
23 | test: /\.js$/,
24 | exclude: /node_modules/,
25 | loader: "eslint-loader",
26 | },
27 | {
28 | test: /\.js$/,
29 | exclude: /node_modules/,
30 | use: "babel-loader",
31 | },
32 | {
33 | test:/\.scss$/,
34 | use:["style-loader","css-loader"],
35 | }
36 | ],
37 | },
38 | plugins: [
39 | new webpack.HotModuleReplacementPlugin(),
40 | new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
41 | new webpack.NoEmitOnErrorsPlugin(),
42 | new UglifyJSPlugin({
43 | sourceMap: true,
44 | uglifyOptions: {
45 | ie8: true,
46 | ecma: 5,
47 | output: {
48 | comments: (astNode, comment) => comment && comment.value && /@jscrambler/.test(comment.value),
49 | beautify: false,
50 | },
51 | compress: {
52 | properties: false,
53 | drop_console: true,
54 | },
55 | },
56 | }),
57 | ],
58 | };
59 |
--------------------------------------------------------------------------------
/src/error.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file error event center
3 | * @author JYkidrecord
4 | * @version 0.0.1-beta
5 | */
6 |
7 | /* eslint-disable */
8 | import { ajax } from "./ajax";
9 | import Wrap from "./wrap";
10 |
11 | const wrap = new Wrap();
12 | const getErrorData = function(err, initMonitor) {
13 | let data = wrap._getErrorMessage(err);
14 | data.record = [];
15 |
16 | let config = initMonitor._config;
17 | if (config.record) {
18 | data.record = initMonitor._getRrwebEvent();
19 | }
20 | return data;
21 | };
22 |
23 | // 服务端返回错误
24 | export const getServerError = function() {};
25 |
26 | // ajaxError
27 | export const ajaxError = function(err, initMonitor) {
28 | // 处理err 上报
29 | if (err.type === "ajaxLoad" && err.detail.status >= 400) {
30 | let data = getErrorData(err, initMonitor);
31 | let config = initMonitor._config;
32 | ajax.post(config.protocol + config.url, data, function() {
33 | initMonitor._clearEvent();
34 | },
35 | function(error) {
36 | console.log(error);
37 | });
38 | }
39 | }
40 |
41 | // js 抛出的错误
42 | export const getJsError = function(err, initMonitor) {
43 | let data = getErrorData(err, initMonitor);
44 | let config = initMonitor._config;
45 | ajax.post(config.protocol + config.url, data,
46 | function() {
47 | initMonitor._clearEvent();
48 | },
49 | function(error) {
50 | console.log(error);
51 | });
52 | }
53 |
54 | // 资源加载错误
55 | export const geetResourceError = function (err, initMonitor) {
56 | let data = getErrorData(err, initMonitor);
57 | let config = initMonitor._config;
58 | ajax.post(config.protocol + config.url, data,
59 | function() {
60 | initMonitor._clearEvent();
61 | },
62 | function(error) {
63 | console.log(error);
64 | });
65 | }
66 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file user account main js in project
3 | * @author JYkid
4 | * @version 0.0.1
5 | */
6 |
7 | const fs = require("fs");
8 | const express = require("express");
9 | const webpack = require("webpack");
10 | const devMiddleware = require("webpack-dev-middleware");
11 | const hotMiddleware = require("webpack-hot-middleware");
12 | const config = require("../config/webpack.dev.config");
13 | const bodyParser = require("body-parser");
14 | const session = require('express-session');
15 | // const { connect } = require('./database/init');
16 |
17 | const app = express();
18 | app.use(express.static("./"));
19 | const complier = webpack(config);
20 |
21 | // (async() => {
22 | // await connect();
23 | // })();
24 |
25 | app.all('*', (req, res, next) => {
26 | res.header('Access-Control-Allow-Origin', '*');
27 | res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
28 | res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
29 | if (req.method == 'OPTIONS') {
30 | res.send(200);
31 | } else {
32 | next();
33 | }
34 | });
35 |
36 |
37 | // webpack middleware
38 | app.use(devMiddleware(complier, {
39 | publicPath: config.output.publicPath,
40 | quiet: true,
41 | }));
42 |
43 | app.use(hotMiddleware(complier));
44 |
45 | app.use(bodyParser.json());
46 | app.use(bodyParser.urlencoded({extended: false}));
47 | app.use(express.static("gt-dist"));
48 | app.use(express.static("build"));
49 |
50 | app.use(session({
51 | secret: 'my-secret',
52 | resave: false,
53 | saveUninitialized: true
54 | }));
55 |
56 | app.use((req, res, next) => {
57 | res.header("Access-Control-Allow-Origin", "*");
58 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
59 | next();
60 | });
61 |
62 | // page URL
63 | app.use("/index", (req, res) => {
64 | const filename = "./index.html";
65 | fs.readFile(filename, (err, result) => {
66 | res.set("content-type", "text/html");
67 | res.send(result);
68 | res.end();
69 | });
70 | });
71 |
72 | // create 500 in server
73 | app.use("/servererr", (req, res) => {
74 | reqq;
75 | res.send({
76 | status: "success",
77 | });
78 | });
79 |
80 | // timeout no res
81 | app.use("/timeout", (req, res) => {});
82 |
83 | // timeout no res
84 | app.use("/monitor", (req, res) => {
85 | res.send({});
86 | });
87 |
88 | const point = 9998;
89 | console.log("Your server listen at " + "http://localhost:" +point + "/index");
90 | app.listen(point);
91 |
92 |
--------------------------------------------------------------------------------
/coverage/lcov-report/block-navigation.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var jumpToCode = (function init() {
3 | // Classes of code we would like to highlight in the file view
4 | var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
5 |
6 | // Elements to highlight in the file listing view
7 | var fileListingElements = ['td.pct.low'];
8 |
9 | // We don't want to select elements that are direct descendants of another match
10 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
11 |
12 | // Selecter that finds elements on the page to which we can jump
13 | var selector =
14 | fileListingElements.join(', ') +
15 | ', ' +
16 | notSelector +
17 | missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
18 |
19 | // The NodeList of matching elements
20 | var missingCoverageElements = document.querySelectorAll(selector);
21 |
22 | var currentIndex;
23 |
24 | function toggleClass(index) {
25 | missingCoverageElements
26 | .item(currentIndex)
27 | .classList.remove('highlighted');
28 | missingCoverageElements.item(index).classList.add('highlighted');
29 | }
30 |
31 | function makeCurrent(index) {
32 | toggleClass(index);
33 | currentIndex = index;
34 | missingCoverageElements.item(index).scrollIntoView({
35 | behavior: 'smooth',
36 | block: 'center',
37 | inline: 'center'
38 | });
39 | }
40 |
41 | function goToPrevious() {
42 | var nextIndex = 0;
43 | if (typeof currentIndex !== 'number' || currentIndex === 0) {
44 | nextIndex = missingCoverageElements.length - 1;
45 | } else if (missingCoverageElements.length > 1) {
46 | nextIndex = currentIndex - 1;
47 | }
48 |
49 | makeCurrent(nextIndex);
50 | }
51 |
52 | function goToNext() {
53 | var nextIndex = 0;
54 |
55 | if (
56 | typeof currentIndex === 'number' &&
57 | currentIndex < missingCoverageElements.length - 1
58 | ) {
59 | nextIndex = currentIndex + 1;
60 | }
61 |
62 | makeCurrent(nextIndex);
63 | }
64 |
65 | return function jump(event) {
66 | switch (event.which) {
67 | case 78: // n
68 | case 74: // j
69 | goToNext();
70 | break;
71 | case 66: // b
72 | case 75: // k
73 | case 80: // p
74 | goToPrevious();
75 | break;
76 | }
77 | };
78 | })();
79 | window.addEventListener('keydown', jumpToCode);
80 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Monitor 打包测试
8 |
20 |
21 |
22 | Monitor 打包测试
23 |
24 |
25 | ajax请求错误
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | js执行错误
35 |
36 |
37 |
38 |
39 | Promise 错误
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 | 资源没有加载
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 第三方资源错误
58 |
59 |
60 |
61 |
62 | 销毁实例
63 |
64 |
65 |
66 |
67 |
93 |
94 |
--------------------------------------------------------------------------------
/coverage/clover.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fe-monitor",
3 | "version": "0.0.1-beta",
4 | "description": "Front End buried point",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "jest",
8 | "lint": "eslint --fix",
9 | "start": "node server/index",
10 | "dev": "node server/index",
11 | "build": "rm -rf ./dist/js && webpack --config config/webpack.pro.config.js && node write.js && conventional-changelog -p angular -i ./CHANGELOG.md -s",
12 | "dist-js": "rm -rf ./dist/js && webpack --config config/webpack.pro.config.js && node write.js && conventional-changelog -p angular -i ./CHANGELOG.md -s",
13 | "dev-js": "rm -rf ./dist/js && webpack --config config/webpack.pro.config.js --env.pro=dev && node write.js && conventional-changelog -p angular -i ./CHANGELOG.md -s",
14 | "server": "node server/server"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/MriLiuJY/FE-Monitor.git"
19 | },
20 | "author": "",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/MriLiuJY/FE-Monitor/issues"
24 | },
25 | "homepage": "https://github.com/MriLiuJY/FE-Monitor#readme",
26 | "dependencies": {
27 | "@babel/core": "^7.4.5",
28 | "@babel/preset-env": "^7.4.5",
29 | "babel-core": "7.0.0-bridge.0",
30 | "babel-eslint": "^10.0.1",
31 | "babel-jest": "^23.6.0",
32 | "babel-loader": "^7.1.5",
33 | "body-parser": "^1.19.0",
34 | "chalk": "^2.4.2",
35 | "es3ify-loader": "^0.2.0",
36 | "eslint-config-airbnb-base": "^13.1.0",
37 | "eslint-loader": "^2.1.2",
38 | "eslint-plugin-import": "^2.17.3",
39 | "fe-monitor-sdk": "0.0.1",
40 | "jest": "^24.8.0",
41 | "mongoose": "^5.6.2",
42 | "rrweb": "^0.7.18",
43 | "uglifyjs-webpack-plugin": "^2.1.3",
44 | "webpack": "^4.34.0",
45 | "webpack-dev-middleware": "^3.7.0",
46 | "webpack-hot-middleware": "^2.25.0"
47 | },
48 | "devDependencies": {
49 | "compression-webpack-plugin": "^3.0.0",
50 | "eslint": "^5.16.0",
51 | "express": "^4.17.1",
52 | "express-session": "^1.16.2",
53 | "husky": "^3.0.3",
54 | "lint-staged": "^9.2.1",
55 | "webpack-cli": "^3.3.4"
56 | },
57 | "config": {
58 | "commitizen": {
59 | "path": "./node_modules/cz-conventional-changelog"
60 | }
61 | },
62 | "jest": {
63 | "moduleFileExtensions": [
64 | "js",
65 | "json"
66 | ],
67 | "transform": {
68 | "^.+\\.js?$": "babel-jest"
69 | },
70 | "moduleNameMapper": {
71 | "^@/(.*)$": "/src/$1"
72 | },
73 | "testMatch": [
74 | "**/test/unit/*.spec.(js|jsx|ts|tsx)|**/__test__/*.(js|jsx|ts|tsx)"
75 | ],
76 | "testURL": "http://localhost/",
77 | "collectCoverage": true,
78 | "collectCoverageFrom": [
79 | "**/src/**/*.js",
80 | "!**/node_modules/**"
81 | ]
82 | },
83 | "gitHooks": {
84 | "pre-commit": "lint-staged",
85 | "commit-msg": "node git-hooks/verify-commit-msg.js"
86 | },
87 | "lint-staged": {
88 | "*.js": [
89 | "eslint --fix",
90 | "git add"
91 | ]
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Monitor testPage
8 |
20 |
21 |
22 | Monitor testPage
23 |
24 |
25 | ajax请求错误
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | js执行错误
35 |
36 |
37 |
38 |
39 | Promise 错误
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 | 资源没有加载
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | 第三方资源错误
58 |
59 |
60 |
61 |
62 | 销毁实例
63 |
64 |
65 |
66 |
67 |
68 | rrweb 重播
69 |
70 |
71 |
72 |
73 |
74 |
75 |
93 |
94 |
95 |
96 |
111 |
112 |
--------------------------------------------------------------------------------
/coverage/lcov-report/error.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for error.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | All files error.js
20 |
21 |
22 |
23 | 0%
24 | Statements
25 | 0/0
26 |
27 |
28 | 0%
29 | Branches
30 | 0/0
31 |
32 |
33 | 0%
34 | Functions
35 | 0/0
36 |
37 |
38 | 0%
39 | Lines
40 | 0/0
41 |
42 |
43 |
44 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
45 |
46 |
47 |
48 |
49 | | 1
50 | 2
51 | 3
52 | 4
53 | 5
54 | 6 |
55 |
56 |
57 |
58 |
59 | | /**
60 | * @file error event center
61 | * @author JYkid
62 | * @version 0.0.1-beta
63 | */
64 | |
65 |
66 |
67 |
68 |
72 |
73 |
74 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | # MonitorFE
2 |
3 | 前端埋点监控SDK,一款开箱即用的前端报错监控的埋点SDK。仅需开发埋点接口即可监控收集整个前端项目在运行的时候产生的js内部报错,资源加载异常,接口返回报错,样式丢失的问题。
4 |
5 | 项目在 SDK 内监听全局 error 错误信息,ajax 错误信息,以及监听资源加载,在页面出现报错的情况下直接向埋点接口上报错误信息,监控前端项目的稳定性。
6 |
7 | ## 设计目的
8 |
9 | 1.方便更多的前端开发快速接入前端埋点。
10 |
11 | 2.为更多中小型的项目增加前端的报错监控。
12 |
13 | 3.作为一款开源产品帮助更多的项目提升稳定性。
14 |
15 | 
16 |
17 | ## 使用
18 |
19 | ### 配置项
20 |
21 | 用于传入 `initMonitor` 的配置项。
22 |
23 | |属性|参数|默认值|可选值|说明|
24 | |:----- |:-------|:-----|:-----|----- |
25 | | method | String | POST | POST,GET | 埋点上报请求方法 |
26 | | url | String | - | - | 埋点上报url地址 |
27 | | id | String | - | - | 标识当前用户 |
28 | | record | Boolean | false | - | 是否录制用户操作用于回放 |
29 |
30 | 可以直接 [下载](https://github.com/MriLiuJY/FE-Monitor/releases) SDK 引入自己项目中即可使用。(dist/js)目录下的打包文件。
31 |
32 | ### 引入方式
33 |
34 | **npm 下载**
35 |
36 | ```
37 | npm install fe-monitor-sdk
38 | ```
39 |
40 | 或者你可以使用淘宝 NPM 镜像安装:
41 |
42 | ```
43 | npm install fe-monitor-sdk --registry=https://registry.npm.taobao.org
44 | ```
45 |
46 | *使用*
47 |
48 | ```js
49 | var initMonitor = require("fe-monitor-sdk");
50 |
51 | var config = {}
52 | // your config
53 | };
54 | initMonitor(config, function(monitor) {
55 | // your code...
56 | // 销毁实例
57 | monitor._destory();
58 | });
59 | ```
60 |
61 | 另外,浏览器并不支持 `CommonJS` 规范,因此需要使用 `Browserify` 或者 `Webpack` 等工具进行打包。
62 |
63 |
64 | **js 文件引入**
65 |
66 | 同源的情况下可以直接引入自己的项目中,注意请使用最新的 SDK 以获取更好的效果。
67 |
68 | ```js
69 |
70 |
71 |
81 | ```
82 |
83 | 如果 JS 放在 CDN 上需要单独引入的情况下需要一些额外的 code 。
84 |
85 | ```js
86 |
104 | ```
105 |
106 | 项目在v0.0.3 版本之后加入 [rrweb](https://github.com/rrweb-io/rrweb) 录制回放功能,可以通过配置传入 `initMonitor` 的 config 中的 `record` 的值来选择是否开启录制,如果传值为 true 的情况下每次错误都会上报收集到的数据用于回放错误复现。
107 |
108 | ## 项目架构
109 |
110 | 首先v0.1.0版本设计目的是为了做完整的异常监控,使得前端具备资源加载异常,js运行报错,样式丢失,接口返回异常的捕获能力。
111 |
112 | 目录结构:
113 | ```
114 | ├── config // webpack 配置文件
115 | ├── dist // 打包输出文件目录
116 | ├── public // 公共文件目录
117 | ├── server // server
118 | ├── src // 项目文件目录
119 | ├── test
120 | ```
121 |
122 | * JS-SDK具备获取平台信息的能力
123 | * 获取报错详情与报错的时间
124 | * 当前的url(完整带参数)
125 | * 用户可以自定义上传的参数
126 |
127 |
128 | ## 开发
129 |
130 | 自动生成changelog 需要在本地下载:
131 |
132 | `npm install -g conventional-changelog-cli`
133 |
134 |
135 | ## License
136 |
137 | [MIT](https://opensource.org/licenses/MIT)
138 |
139 | Copyright (c) 2019 MriLiuJY
140 |
--------------------------------------------------------------------------------
/coverage/lcov-report/stateChange.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for stateChange.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | All files stateChange.js
20 |
21 |
22 |
23 | 0%
24 | Statements
25 | 0/0
26 |
27 |
28 | 0%
29 | Branches
30 | 0/0
31 |
32 |
33 | 0%
34 | Functions
35 | 0/0
36 |
37 |
38 | 0%
39 | Lines
40 | 0/0
41 |
42 |
43 |
44 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
45 |
46 |
47 |
48 |
49 | | 1
50 | 2
51 | 3
52 | 4
53 | 5
54 | 6 |
55 |
56 |
57 |
58 |
59 | | /**
60 | * @file event center
61 | * @author JYkid
62 | * @version 0.0.1-beta
63 | */
64 | |
65 |
66 |
67 |
68 |
72 |
73 |
74 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MonitorFE
2 |
3 | 前端埋点监控SDK,一款开箱即用的前端报错监控的埋点SDK。仅需开发埋点接口即可监控收集整个前端项目在运行的时候产生的js内部报错,资源加载异常,接口返回报错,样式丢失的问题。
4 |
5 | 项目在 SDK 内监听全局 error 错误信息,ajax 错误信息,以及监听资源加载,在页面出现报错的情况下直接向埋点接口上报错误信息,监控前端项目的稳定性。
6 |
7 | [English document](https://github.com/MriLiuJY/monitorFE/blob/master/docs/README_EN.md)
8 |
9 | ## 设计目的
10 |
11 | 1.方便更多的前端开发快速接入前端埋点。
12 |
13 | 2.为更多中小型的项目增加前端的报错监控。
14 |
15 | 3.作为一款开源产品帮助更多的项目提升稳定性。
16 |
17 | 
18 |
19 | ## 使用
20 |
21 | ### 配置项
22 |
23 | 用于传入 `initMonitor` 的配置项。
24 |
25 | |属性|参数|默认值|可选值|说明|
26 | |:----- |:-------|:-----|:-----|----- |
27 | | method | String | POST | POST,GET | 埋点上报请求方法 |
28 | | url | String | - | - | 埋点上报url地址 |
29 | | id | String | - | - | 标识当前用户 |
30 | | record | Boolean | false | - | 是否录制用户操作用于回放 |
31 |
32 | 可以直接 [下载](https://github.com/MriLiuJY/FE-Monitor/releases) SDK 引入自己项目中即可使用。(dist/js)目录下的打包文件。
33 |
34 | ### 引入方式
35 |
36 | **npm 下载**
37 |
38 | ```
39 | npm install fe-monitor-sdk
40 | ```
41 |
42 | 或者你可以使用淘宝 NPM 镜像安装:
43 |
44 | ```
45 | npm install fe-monitor-sdk --registry=https://registry.npm.taobao.org
46 | ```
47 |
48 | *使用*
49 |
50 | ```js
51 | var initMonitor = require("fe-monitor-sdk");
52 |
53 | var config = {}
54 | // your config
55 | };
56 | initMonitor(config, function(monitor) {
57 | // your code...
58 | // 销毁实例
59 | monitor._destory();
60 | });
61 | ```
62 |
63 | 另外,浏览器并不支持 `CommonJS` 规范,因此需要使用 `Browserify` 或者 `Webpack` 等工具进行打包。
64 |
65 |
66 | **js 文件引入**
67 |
68 | 同源的情况下可以直接引入自己的项目中,注意请使用最新的 SDK 以获取更好的效果。
69 |
70 | ```js
71 |
72 |
73 |
83 | ```
84 |
85 | 如果 JS 放在 CDN 上需要单独引入的情况下需要一些额外的 code 。
86 |
87 | ```js
88 |
106 | ```
107 |
108 | 项目在v0.0.3 版本之后加入 [rrweb](https://github.com/rrweb-io/rrweb) 录制回放功能,可以通过配置传入 `initMonitor` 的 config 中的 `record` 的值来选择是否开启录制,如果传值为 true 的情况下每次错误都会上报收集到的数据用于回放错误复现。
109 |
110 | 请注意开启 rrweb 会有部分兼容问题请注意:
111 |
112 | > 由于使用 `MutationObserver` API,rrweb 不支持 IE11 以下的浏览器。可以从[这里](https://caniuse.com/#feat=mutationobserver)找到兼容的浏览器列表。
113 |
114 | ## 项目架构
115 |
116 | 首先v0.1.0版本设计目的是为了做完整的异常监控,使得前端具备资源加载异常,js运行报错,样式丢失,接口返回异常的捕获能力。
117 |
118 | 目录结构:
119 | ```
120 | ├── config // webpack 配置文件
121 | ├── dist // 打包输出文件目录
122 | ├── public // 公共文件目录
123 | ├── server // server
124 | ├── src // 项目文件目录
125 | ├── test
126 | ```
127 |
128 | * JS-SDK具备获取平台信息的能力
129 | * 获取报错详情与报错的时间
130 | * 当前的url(完整带参数)
131 | * 用户可以自定义上传的参数
132 |
133 |
134 | ## 开发
135 |
136 | 自动生成changelog 需要在本地下载:
137 |
138 | `npm install -g conventional-changelog-cli`
139 |
140 |
141 | ## License
142 |
143 | [MIT](https://opensource.org/licenses/MIT)
144 |
145 | Copyright (c) 2019 MriLiuJY
146 |
--------------------------------------------------------------------------------
/docs/README_ZH.md:
--------------------------------------------------------------------------------
1 | # MonitorFE
2 |
3 | 前端埋点监控SDK,一款开箱即用的前端报错监控的埋点SDK。仅需开发埋点接口即可监控收集整个前端项目在运行的时候产生的js内部报错,资源加载异常,接口返回报错,样式丢失的问题。
4 |
5 | 项目在 SDK 内监听全局 error 错误信息,ajax 错误信息,以及监听资源加载,在页面出现报错的情况下直接向埋点接口上报错误信息,监控前端项目的稳定性。
6 |
7 | [English document](https://github.com/MriLiuJY/monitorFE/blob/master/docs/README_EN.md)
8 |
9 | ## 设计目的
10 |
11 | 1.方便更多的前端开发快速接入前端埋点。
12 |
13 | 2.为更多中小型的项目增加前端的报错监控。
14 |
15 | 3.作为一款开源产品帮助更多的项目提升稳定性。
16 |
17 | 
18 |
19 | ## 使用
20 |
21 | ### 配置项
22 |
23 | 用于传入 `initMonitor` 的配置项。
24 |
25 | |属性|参数|默认值|可选值|说明|
26 | |:----- |:-------|:-----|:-----|----- |
27 | | method | String | POST | POST,GET | 埋点上报请求方法 |
28 | | url | String | - | - | 埋点上报url地址 |
29 | | id | String | - | - | 标识当前用户 |
30 | | record | Boolean | false | - | 是否录制用户操作用于回放 |
31 |
32 | 可以直接 [下载](https://github.com/MriLiuJY/FE-Monitor/releases) SDK 引入自己项目中即可使用。(dist/js)目录下的打包文件。
33 |
34 | ### 引入方式
35 |
36 | **npm 下载**
37 |
38 | ```
39 | npm install fe-monitor-sdk
40 | ```
41 |
42 | 或者你可以使用淘宝 NPM 镜像安装:
43 |
44 | ```
45 | npm install fe-monitor-sdk --registry=https://registry.npm.taobao.org
46 | ```
47 |
48 | *使用*
49 |
50 | ```js
51 | var initMonitor = require("fe-monitor-sdk");
52 |
53 | var config = {}
54 | // your config
55 | };
56 | initMonitor(config, function(monitor) {
57 | // your code...
58 | // 销毁实例
59 | monitor._destory();
60 | });
61 | ```
62 |
63 | 另外,浏览器并不支持 `CommonJS` 规范,因此需要使用 `Browserify` 或者 `Webpack` 等工具进行打包。
64 |
65 |
66 | **js 文件引入**
67 |
68 | 同源的情况下可以直接引入自己的项目中,注意请使用最新的 SDK 以获取更好的效果。
69 |
70 | ```js
71 |
72 |
73 |
83 | ```
84 |
85 | 如果 JS 放在 CDN 上需要单独引入的情况下需要一些额外的 code 。
86 |
87 | ```js
88 |
106 | ```
107 |
108 | 项目在v0.0.3 版本之后加入 [rrweb](https://github.com/rrweb-io/rrweb) 录制回放功能,可以通过配置传入 `initMonitor` 的 config 中的 `record` 的值来选择是否开启录制,如果传值为 true 的情况下每次错误都会上报收集到的数据用于回放错误复现。
109 |
110 | 请注意开启 rrweb 会有部分兼容问题请注意:
111 |
112 | > 由于使用 `MutationObserver` API,rrweb 不支持 IE11 以下的浏览器。可以从[这里](https://caniuse.com/#feat=mutationobserver)找到兼容的浏览器列表。
113 |
114 | ## 项目架构
115 |
116 | 首先v0.1.0版本设计目的是为了做完整的异常监控,使得前端具备资源加载异常,js运行报错,样式丢失,接口返回异常的捕获能力。
117 |
118 | 目录结构:
119 | ```
120 | ├── config // webpack 配置文件
121 | ├── dist // 打包输出文件目录
122 | ├── public // 公共文件目录
123 | ├── server // server
124 | ├── src // 项目文件目录
125 | ├── test
126 | ```
127 |
128 | * JS-SDK具备获取平台信息的能力
129 | * 获取报错详情与报错的时间
130 | * 当前的url(完整带参数)
131 | * 用户可以自定义上传的参数
132 |
133 |
134 | ## 开发
135 |
136 | 自动生成changelog 需要在本地下载:
137 |
138 | `npm install -g conventional-changelog-cli`
139 |
140 |
141 | ## License
142 |
143 | [MIT](https://opensource.org/licenses/MIT)
144 |
145 | Copyright (c) 2019 MriLiuJY
146 |
--------------------------------------------------------------------------------
/coverage/lcov-report/config.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for config.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | All files config.js
20 |
21 |
22 |
23 | 0%
24 | Statements
25 | 0/0
26 |
27 |
28 | 0%
29 | Branches
30 | 0/0
31 |
32 |
33 | 0%
34 | Functions
35 | 0/1
36 |
37 |
38 | 0%
39 | Lines
40 | 0/0
41 |
42 |
43 |
44 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
45 |
46 |
47 |
48 |
49 | | 1
50 | 2
51 | 3
52 | 4
53 | 5
54 | 6
55 | 7
56 | 8
57 | 9 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | | /**
66 | * @file config detail file
67 | * @author JYkid
68 | * @version 0.0.1-beta
69 | */
70 |
71 | function Config(conf) {
72 |
73 | } |
74 |
75 |
76 |
77 |
81 |
82 |
83 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at mriliujy@gmail.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/createError.js:
--------------------------------------------------------------------------------
1 |
2 | // ----- ajax请求错误 ----
3 |
4 | // ajax请求错误
5 | var ajaxRequestError = document.getElementsByClassName("err-ajax-request")[0];
6 | ajaxRequestError.onclick = function () {
7 | var xhr = new XMLHttpRequest();
8 | xhr.withCredentials = true;
9 | xhr.timeout = 3000;
10 | xhr.open("get", '/ajaxerror', true);
11 | xhr.setRequestHeader("content-type", "application/json;charset=utf-8");
12 | xhr.setRequestHeader("Accept", "application/json");
13 | xhr.send();
14 | }
15 |
16 | // server 500 error
17 | var servererrAjax = document.getElementsByClassName("servererr-ajax-request")[0];
18 | servererrAjax.onclick = function () {
19 | var xhr = new XMLHttpRequest();
20 | xhr.withCredentials = true;
21 | xhr.timeout = 3000;
22 | xhr.open("get", '/servererr', true);
23 | xhr.setRequestHeader("content-type", "application/json;charset=utf-8");
24 | xhr.setRequestHeader("Accept", "application/json");
25 | xhr.send();
26 | }
27 |
28 | // ajax失败
29 | var ajaxFailed = document.getElementsByClassName("fail-ajax-request")[0];
30 | ajaxFailed.onclick = function () {
31 | var xhr = new XMLHttpRequest();
32 | xhr.withCredentials = true;
33 | xhr.timeout = 3000;
34 | xhr.open("get", '/servererr', true);
35 | xhr.setRequestHeader("content-type", "application/json;charset=utf-8");
36 | xhr.setRequestHeader("Accept", "application/json");
37 | xhr.send();
38 | }
39 |
40 | // ajax请求超时
41 | var ajaxTimeout = document.getElementsByClassName("timeout-ajax-request")[0];
42 | ajaxTimeout.onclick = function () {
43 | var xhr = new XMLHttpRequest();
44 | xhr.withCredentials = true;
45 | xhr.timeout = 3000;
46 | xhr.open("get", '/timeout', true);
47 | xhr.setRequestHeader("content-type", "application/json;charset=utf-8");
48 | xhr.setRequestHeader("Accept", "application/json");
49 | xhr.send();
50 | }
51 |
52 |
53 | // ----- js 执行错误 ----
54 |
55 | // js 执行错误
56 | var jsRunningerror = document.getElementsByClassName("js-running-error")[0];
57 | jsRunningerror.onclick = function () {
58 | jsRunningerrorssss;
59 | }
60 |
61 | // Promise 错误
62 | var promiseError = document.getElementsByClassName("promise-error")[0];
63 | promiseError.onclick = function () {
64 | new Promise((resolve, reject) => {
65 | reject();
66 | })
67 | }
68 |
69 |
70 | // ----- 资源加载异常 ----
71 |
72 | // js
73 | var jsload = document.getElementsByClassName("err-js-load")[0];
74 | jsload.onclick = function () {
75 | var script = document.createElement("script");
76 | script.src = `./js/undefied.js`;
77 | document.body.appendChild(script);
78 | }
79 |
80 | var cssload = document.getElementsByClassName("err-css-load")[0];
81 | cssload.onclick = function () {
82 | var css = document.createElement("link");
83 | css.type = `text/css`;
84 | css.rel = 'stylesheet';
85 | css.href = `./js/undefied.css`;
86 | document.head.appendChild(css);
87 | }
88 |
89 | var imageload = document.getElementsByClassName("err-image-load")[0];
90 | imageload.onclick = function () {
91 | var img = document.createElement("img");
92 | img.src = `./js/undefied.png`;
93 | document.body.appendChild(img);
94 | }
95 |
96 | var iframeload = document.getElementsByClassName("err-iframe-load")[0];
97 | iframeload.onclick = function () {
98 | var iframe = document.createElement("iframe");
99 | iframe.src = `./js/undefied.html`;
100 | document.body.appendChild(iframe);
101 | }
102 |
103 |
104 | // ------ 第三方资源错误 ------
105 | var resourceError = document.getElementsByClassName("other-resource-error")[0];
106 | resourceError.onclick = function () {
107 | var test = document.getElementsByClassName("other-resource-error111")[0];
108 | test.onclick= function() {};
109 | }
110 |
111 | // ------ 销毁实例 ------
112 | var destory = document.getElementsByClassName("destory")[0];
113 | destory.onclick = function () {
114 | window.monitor._destory();
115 | }
116 |
--------------------------------------------------------------------------------
/src/wrap.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file get browser && platform parameter
3 | * @author JYkid
4 | * @version 0.0.1-beta
5 | */
6 |
7 | /* eslint-disable */
8 | function Wrap() {
9 | const self = this;
10 | self.data = self._geWrap();
11 | }
12 |
13 | Wrap.prototype = {
14 | // 获取 UA 基本信息
15 | _geWrap: function() {
16 | let data = {};
17 | let navigator = window.navigator;
18 |
19 | // UA
20 | data.userAgent = navigator.userAgent;
21 |
22 | // appName
23 | data.appName= navigator.appName;
24 |
25 | // appVersion
26 | data.appVersion = navigator.appVersion;
27 |
28 | // CPU
29 | data.cpuClass = navigator.cpuClass;
30 |
31 | // platform
32 | data.platform = navigator.platform;
33 |
34 | // product
35 | data.product = navigator.product;
36 |
37 | // languages
38 | data.language = navigator.language;
39 |
40 | // url
41 | data.url = window.location.href;
42 |
43 | // time
44 | data.time = (new Date()).getTime();
45 |
46 | return data;
47 | },
48 | // webrtc 获取 IP
49 | _getIP: function(onNewIP) {
50 | var myPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
51 | var pc = new myPeerConnection({
52 | iceServers: [
53 | {
54 | urls: "stun:stun01.sipphone.com"
55 | },
56 | {
57 | urls: "stun:stun.ekiga.net"
58 | },
59 | {
60 | urls: "stun:stun.fwdnet.net"
61 | },
62 | {
63 | urls: "stun:stun.l.google.com:19302"
64 | },
65 | {
66 | urls: "stun:stun.l.google.com:19302"
67 | },
68 | {
69 | urls: "stun:stun.l.google.com:19302"
70 | }
71 | ]
72 | }),
73 | noop = function() {},
74 | localIPs = {},
75 | ipRegex = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g;
76 |
77 | function iterateIP(ip) {
78 | if (!localIPs[ip]) {
79 | onNewIP(ip);
80 | }
81 | localIPs[ip] = true;
82 | }
83 |
84 | //create a bogus data channel
85 | pc.createDataChannel("");
86 |
87 | // create offer and set local description
88 | pc.createOffer().then(function(sdp) {
89 | sdp.sdp.split('\n').forEach(function(line) {
90 | if (line.indexOf('candidate') < 0) return;
91 | line.match(ipRegex).forEach(iterateIP);
92 | });
93 |
94 | pc.setLocalDescription(sdp, noop, noop);
95 | }).catch(function(reason) {
96 | // An error occurred, so handle the failure to connect
97 | });
98 |
99 | //listen for candidate events
100 | pc.onicecandidate = function(ice) {
101 | if (!ice || !ice.candidate || !ice.candidate.candidate || !ice.candidate.candidate.match(ipRegex)) return;
102 | ice.candidate.candidate.match(ipRegex).forEach(iterateIP);
103 | };
104 | },
105 | // 处理错误信息
106 | _getErrorMessage: function(err, resource) {
107 | const self = this;
108 | let data = self._geWrap();
109 | self._getIP(function(ip) {
110 | data.ip = ip;
111 | });
112 | data.detail = {};
113 | if (err.type === "ajaxLoad") {
114 | data.detail.responseURL = err.detail.responseURL;
115 | data.detail.status = err.detail.status;
116 | data.detail.statusText = err.detail.statusText;
117 | data.detail.type = "ajaxLoad";
118 | } else if (err.type === "error") {
119 | data.detail.message = err.message;
120 | data.detail.line = err.lineno;
121 | data.detail.filename = err.filename;
122 | data.detail.type = "error";
123 | } else if (resource) {
124 | data.detail.src = err.target.src;
125 | data.detail.type = "resource";
126 | }
127 | // data.jsStack = self._getCallStack();
128 | return data;
129 | },
130 | _getCallStack() {
131 | var stack = "#", total = 0, fn =arguments.callee;
132 | while ( (fn = fn.caller) ) {
133 | stack = stack + "" + fn.name;
134 | total++
135 | }
136 | return stack;
137 | },
138 | }
139 |
140 | module.exports = Wrap;
--------------------------------------------------------------------------------
/src/initMonitor.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file init func
3 | * @author JYkid
4 | * @version 0.0.1-beta
5 | */
6 |
7 | import { record } from "rrweb";
8 | import Config from "./config";
9 | import { getJsError, geetResourceError, ajaxError } from "./error";
10 | import EventCenter from "./eventCenter";
11 |
12 | /* eslint-disable */
13 | function InitMonitor(userConfig) {
14 | let self = this;
15 | let config = new Config(userConfig);
16 | self._config = config;
17 |
18 | self._config.protocol = window.location.protocol + "//";
19 | if (config.https) {
20 | self._config.protocol = 'https://';
21 | }
22 | const eventCenter = new EventCenter();
23 | self._eventCenter = eventCenter;
24 |
25 | self._initListenJS();
26 | self._initListenAjax();
27 | if (userConfig.record) {
28 | self._initRrweb();
29 | }
30 | }
31 |
32 | InitMonitor.prototype = {
33 | _initListenJS() {
34 | const self = this;
35 |
36 | // 监听全局下的 Promise 错误
37 | let unhandledrejection = function(err){
38 | getJsError(err, self);
39 | }
40 | window.addEventListener("unhandledrejection", unhandledrejection);
41 | self._setEvent({
42 | type: "unhandledrejection",
43 | func: unhandledrejection
44 | });
45 |
46 | // 监听全局下的error事件
47 | let errorEvent = function(err) {
48 |
49 | if (err.cancelable) {
50 | // 判断错误是否来自 monitor
51 | if (err.filename.indexOf('monitor') > -1 || process.env.NODE_ENV === 'development') {
52 | return;
53 | } else {
54 | getJsError(err, self);
55 | }
56 | } else {
57 | // 静态资源加载的error事件
58 | geetResourceError(err, self);
59 | }
60 | }
61 | window.addEventListener("error", errorEvent, true);
62 | self._setEvent({
63 | type: "error",
64 | func: errorEvent
65 | });
66 | },
67 | _initListenAjax () {
68 | let self = this;
69 | function ajaxEventTrigger(event) {
70 | var ajaxEvent = new CustomEvent(event, { detail: this });
71 | window.dispatchEvent(ajaxEvent);
72 | };
73 |
74 | var oldXHR = window.XMLHttpRequest;
75 |
76 | function newXHR() {
77 | var realXHR = new oldXHR();
78 |
79 | realXHR.addEventListener('load', function () {
80 | ajaxEventTrigger.call(this, 'ajaxLoad');
81 | }, false);
82 |
83 | realXHR.addEventListener('timeout', function () {
84 | ajaxEventTrigger.call(this, 'ajaxTimeout');
85 | }, false);
86 |
87 | realXHR.addEventListener('readystatechange', function() {
88 | ajaxEventTrigger.call(this, 'ajaxReadyStateChange');
89 | }, false);
90 |
91 | return realXHR;
92 | };
93 |
94 | window.XMLHttpRequest = newXHR;
95 | self._startLintenAjax();
96 | },
97 | _startLintenAjax() {
98 | const self = this;
99 |
100 | // ajax timeout
101 | let ajaxTimeout = function(err) {
102 | !(err.detail.responseURL.indexOf(self._config.url) > -1) && ajaxError(err, self);
103 | };
104 | window.addEventListener("ajaxTimeout", ajaxTimeout);
105 | self._setEvent({
106 | type: "ajaxTimeout",
107 | func: ajaxTimeout
108 | });
109 |
110 | // ajax load error
111 | let ajaxLoad = function(err) {
112 | !(err.detail.responseURL.indexOf(self._config.url) > -1) && ajaxError(err, self);
113 | }
114 | window.addEventListener("ajaxLoad", ajaxLoad);
115 | self._setEvent({
116 | type: "ajaxLoad",
117 | func: ajaxLoad
118 | });
119 | },
120 | _getEvent() {
121 | const self = this;
122 | return self._eventCenter._get();
123 | },
124 | _getRrwebEvent() {
125 | const self = this;
126 | return self._eventCenter._getRecord();
127 | },
128 | _setEvent: function(event) {
129 | const self = this;
130 | self._eventCenter._set(event);
131 | },
132 | /**
133 | * clear rrweb event
134 | */
135 | _clearEvent() {
136 | const self = this;
137 | self._eventCenter._clearRecord();
138 | },
139 | /**
140 | * init rrweb
141 | */
142 | _initRrweb() {
143 | const self = this;
144 | /**
145 | * init record function to record event in browser
146 | * @event mouseevent
147 | * @param { Object } event
148 | */
149 | record({
150 | emit(event) {
151 | self._eventCenter._setRecord(event);
152 | }
153 | });
154 | }
155 | }
156 |
157 | module.exports = InitMonitor;
--------------------------------------------------------------------------------
/coverage/lcov-report/index.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for index.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | All files index.js
20 |
21 |
22 |
23 | 100%
24 | Statements
25 | 4/4
26 |
27 |
28 | 100%
29 | Branches
30 | 0/0
31 |
32 |
33 | 100%
34 | Functions
35 | 1/1
36 |
37 |
38 | 100%
39 | Lines
40 | 4/4
41 |
42 |
43 |
44 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
45 |
46 |
47 |
48 |
49 | | 1
50 | 2
51 | 3
52 | 4
53 | 5
54 | 6
55 | 7
56 | 8
57 | 9
58 | 10
59 | 11
60 | 12
61 | 13
62 | 14
63 | 15
64 | 16
65 | 17 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | 1x
75 | 1x
76 |
77 |
78 | 1x
79 |
80 | 1x
81 | | /**
82 | * @file main js at project
83 | * @author JYkid
84 | * @version 0.0.1-beta
85 | */
86 |
87 |
88 | /* eslint-disable */
89 | function initMonitor(config) {
90 | console.log(config);
91 | return config + 1;
92 | }
93 |
94 | window.initMonitor = initMonitor;
95 |
96 | module.exports = initMonitor;
97 | |
98 |
99 |
100 |
101 |
105 |
106 |
107 |
114 |
115 |
116 |
117 |
118 |
--------------------------------------------------------------------------------
/docs/README_EN.md:
--------------------------------------------------------------------------------
1 | # MonitorFE
2 |
3 | The front-end buried point monitoring SDK, an out-of-the-box front-end error monitoring buried point SDK. Only need to develop the buried interface to monitor and collect the internal error of the js generated during the running of the entire front-end project, the resource loading is abnormal, the interface returns an error, and the style is lost.
4 |
5 | The project listens for global error error information, ajax error information, and listener resource loading in the SDK. When an error occurs on the page, the error message is directly reported to the buried interface to monitor the stability of the front-end project.
6 |
7 | [中文文档](https://github.com/MriLiuJY/monitorFE/blob/master/docs/README_ZH.md)
8 |
9 | ## aim of design
10 |
11 | 1. Convenient for more front-end development and quick access to the front-end buried point.
12 |
13 | 2. Increase front-end error monitoring for more small and medium-sized projects.
14 |
15 | 3. As an open source product to help more projects improve stability.
16 |
17 | 
18 |
19 | ## Use
20 |
21 | ### Configuration Item
22 |
23 | A configuration item for passing in `initMonitor`.
24 |
25 | |Attributes|Parameters|Defaults|Optional Values|Description|
26 | |:----- |:-------|:-----|:-----|----- |
27 | | method | String | POST | POST,GET | Buried Point Reporting Request Method |
28 | | url | String | - | - | Buried point report url address |
29 | | id | String | - | - | Identifies the current user |
30 | | record | Boolean | false | - | Whether to record user actions for playback |
31 |
32 | You can use the [Download](https://github.com/MriLiuJY/FE-Monitor/releases) SDK to bring it into your own project. Package files in the (dist/js) directory.
33 |
34 | ### Introduction method
35 |
36 | **npm download**
37 |
38 | ```
39 | Npm install fe-monitor-sdk
40 | ```
41 |
42 | Or you can use the Taobao NPM image to install:
43 |
44 | ```
45 | Npm install fe-monitor-sdk --registry=https://registry.npm.taobao.org
46 | ```
47 |
48 | *use*
49 |
50 | ```js
51 | Var initMonitor = require("fe-monitor-sdk");
52 |
53 | Var config = {}
54 | // your config
55 | };
56 | initMonitor(config, function(monitor) {
57 | // your code...
58 | // Destroy the instance
59 | Monitor._destory();
60 | });
61 | ```
62 |
63 | In addition, the browser does not support the `CommonJS` specification, so it needs to be packaged using tools such as `Browserify` or `Webpack`.
64 |
65 |
66 | **js file introduction**
67 |
68 | If you have the same origin, you can directly import it into your own project. Please use the latest SDK for better results.
69 |
70 | ```js
71 |
72 |
73 |
83 | ```
84 |
85 | If the JS is placed on the CDN and needs to be introduced separately, some extra code is needed.
86 |
87 | ```js
88 |
106 | ```
107 |
108 | The project adds [rrweb](https://github.com/rrweb-io/rrweb) to the recording playback function after v0.0.3. You can choose whether to pass the value of `record` in the config of `initMonitor`. Turn on recording. If the value is true, the error will report the collected data for playback error replay.
109 |
110 | Please note that there are some compatibility issues when opening rrweb. Please note:
111 |
112 | > Due to the `MutationObserver` API, rrweb does not support browsers below IE11. A list of compatible browsers can be found from [here](https://caniuse.com/#feat=mutationobserver).
113 |
114 | ## Project Architecture
115 |
116 | Firstly, the v0.1.0 version is designed to perform complete exception monitoring, so that the front end has resource loading exceptions, js runs error reporting, style is lost, and the interface returns an abnormal capture capability.
117 |
118 | Directory Structure:
119 | ```
120 | ├── config // webpack configuration file
121 | ├── dist // package output file directory
122 | ├── public // public file directory
123 | ├── server // server
124 | ├── src // project file directory
125 | ├── test
126 | ```
127 |
128 | * JS-SDK has the ability to obtain platform information
129 | * Get the details of the error and the time of the error
130 | * Current url (complete with parameters)
131 | * Users can customize the uploaded parameters
132 |
133 |
134 | ## Development
135 |
136 | Automatically generating a changelog needs to be downloaded locally:
137 |
138 | `npm install -g conventional-changelog-cli`
139 |
140 |
141 | ## License
142 |
143 | [MIT](https://opensource.org/licenses/MIT)
144 |
145 | Copyright (c) 2019 MriLiuJY
--------------------------------------------------------------------------------
/coverage/coverage-final.json:
--------------------------------------------------------------------------------
1 | {"/Users/jyliu/mycode/FE-Monitor/src/ajax.js": {"path":"/Users/jyliu/mycode/FE-Monitor/src/ajax.js","statementMap":{"0":{"start":{"line":7,"column":14},"end":{"line":40,"column":4}},"1":{"start":{"line":8,"column":2},"end":{"line":39,"column":3}},"2":{"start":{"line":10,"column":6},"end":{"line":10,"column":52}},"3":{"start":{"line":13,"column":16},"end":{"line":13,"column":36}},"4":{"start":{"line":14,"column":6},"end":{"line":14,"column":39}},"5":{"start":{"line":15,"column":6},"end":{"line":15,"column":77}},"6":{"start":{"line":16,"column":6},"end":{"line":16,"column":57}},"7":{"start":{"line":17,"column":6},"end":{"line":17,"column":33}},"8":{"start":{"line":18,"column":6},"end":{"line":18,"column":37}},"9":{"start":{"line":19,"column":6},"end":{"line":24,"column":8}},"10":{"start":{"line":20,"column":21},"end":{"line":20,"column":56}},"11":{"start":{"line":21,"column":8},"end":{"line":23,"column":9}},"12":{"start":{"line":22,"column":10},"end":{"line":22,"column":24}},"13":{"start":{"line":25,"column":6},"end":{"line":36,"column":8}},"14":{"start":{"line":26,"column":8},"end":{"line":35,"column":9}},"15":{"start":{"line":27,"column":10},"end":{"line":34,"column":11}},"16":{"start":{"line":28,"column":25},"end":{"line":28,"column":60}},"17":{"start":{"line":29,"column":12},"end":{"line":31,"column":13}},"18":{"start":{"line":30,"column":14},"end":{"line":30,"column":28}},"19":{"start":{"line":33,"column":12},"end":{"line":33,"column":45}},"20":{"start":{"line":37,"column":6},"end":{"line":37,"column":44}},"21":{"start":{"line":42,"column":0},"end":{"line":44,"column":1}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":7,"column":15},"end":{"line":7,"column":16}},"loc":{"start":{"line":7,"column":26},"end":{"line":40,"column":1}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":9,"column":13},"end":{"line":9,"column":14}},"loc":{"start":{"line":9,"column":24},"end":{"line":11,"column":5}},"line":9},"2":{"name":"(anonymous_2)","decl":{"start":{"line":12,"column":10},"end":{"line":12,"column":11}},"loc":{"start":{"line":12,"column":47},"end":{"line":38,"column":5}},"line":12},"3":{"name":"(anonymous_3)","decl":{"start":{"line":19,"column":19},"end":{"line":19,"column":20}},"loc":{"start":{"line":19,"column":31},"end":{"line":24,"column":7}},"line":19},"4":{"name":"(anonymous_4)","decl":{"start":{"line":25,"column":31},"end":{"line":25,"column":32}},"loc":{"start":{"line":25,"column":43},"end":{"line":36,"column":7}},"line":25}},"branchMap":{"0":{"loc":{"start":{"line":10,"column":14},"end":{"line":10,"column":50}},"type":"binary-expr","locations":[{"start":{"line":10,"column":14},"end":{"line":10,"column":35}},{"start":{"line":10,"column":39},"end":{"line":10,"column":50}}],"line":10},"1":{"loc":{"start":{"line":18,"column":20},"end":{"line":18,"column":36}},"type":"binary-expr","locations":[{"start":{"line":18,"column":20},"end":{"line":18,"column":27}},{"start":{"line":18,"column":31},"end":{"line":18,"column":36}}],"line":18},"2":{"loc":{"start":{"line":21,"column":8},"end":{"line":23,"column":9}},"type":"if","locations":[{"start":{"line":21,"column":8},"end":{"line":23,"column":9}},{"start":{"line":21,"column":8},"end":{"line":23,"column":9}}],"line":21},"3":{"loc":{"start":{"line":26,"column":8},"end":{"line":35,"column":9}},"type":"if","locations":[{"start":{"line":26,"column":8},"end":{"line":35,"column":9}},{"start":{"line":26,"column":8},"end":{"line":35,"column":9}}],"line":26},"4":{"loc":{"start":{"line":27,"column":10},"end":{"line":34,"column":11}},"type":"if","locations":[{"start":{"line":27,"column":10},"end":{"line":34,"column":11}},{"start":{"line":27,"column":10},"end":{"line":34,"column":11}}],"line":27},"5":{"loc":{"start":{"line":29,"column":12},"end":{"line":31,"column":13}},"type":"if","locations":[{"start":{"line":29,"column":12},"end":{"line":31,"column":13}},{"start":{"line":29,"column":12},"end":{"line":31,"column":13}}],"line":29}},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0]},"hash":"1a6186719c9fc9ac01a33da497b96c1bc56d332f"}
2 | ,"/Users/jyliu/mycode/FE-Monitor/src/config.js": {"path":"/Users/jyliu/mycode/FE-Monitor/src/config.js","statementMap":{},"fnMap":{"0":{"name":"Config","decl":{"start":{"line":7,"column":9},"end":{"line":7,"column":15}},"loc":{"start":{"line":7,"column":22},"end":{"line":9,"column":1}},"line":7}},"branchMap":{},"s":{},"f":{"0":0},"b":{},"hash":"39da93aab67732a303764e4ec08f894dd8bd2321"}
3 | ,"/Users/jyliu/mycode/FE-Monitor/src/error.js": {"path":"/Users/jyliu/mycode/FE-Monitor/src/error.js","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"hash":"2e3a440bcc5d0cb761cfa5d9d80946e6b3929b42"}
4 | ,"/Users/jyliu/mycode/FE-Monitor/src/index.js": {"path":"/Users/jyliu/mycode/FE-Monitor/src/index.js","statementMap":{"0":{"start":{"line":10,"column":2},"end":{"line":10,"column":22}},"1":{"start":{"line":11,"column":2},"end":{"line":11,"column":20}},"2":{"start":{"line":14,"column":0},"end":{"line":14,"column":33}},"3":{"start":{"line":16,"column":0},"end":{"line":16,"column":29}}},"fnMap":{"0":{"name":"initMonitor","decl":{"start":{"line":9,"column":9},"end":{"line":9,"column":20}},"loc":{"start":{"line":9,"column":29},"end":{"line":12,"column":1}},"line":9}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1},"f":{"0":1},"b":{},"_coverageSchema":"43e27e138ebf9cfc5966b082cf9a028302ed4184","hash":"e2b92b614b38987ba18ed7e3046fb1d373bb9c49"}
5 | ,"/Users/jyliu/mycode/FE-Monitor/src/stateChange.js": {"path":"/Users/jyliu/mycode/FE-Monitor/src/stateChange.js","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"hash":"24ed9a35602110a8a6f5a0494027aac05c66460d"}
6 | }
7 |
--------------------------------------------------------------------------------
/coverage/lcov-report/sorter.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | var addSorting = (function() {
3 | 'use strict';
4 | var cols,
5 | currentSort = {
6 | index: 0,
7 | desc: false
8 | };
9 |
10 | // returns the summary table element
11 | function getTable() {
12 | return document.querySelector('.coverage-summary');
13 | }
14 | // returns the thead element of the summary table
15 | function getTableHeader() {
16 | return getTable().querySelector('thead tr');
17 | }
18 | // returns the tbody element of the summary table
19 | function getTableBody() {
20 | return getTable().querySelector('tbody');
21 | }
22 | // returns the th element for nth column
23 | function getNthColumn(n) {
24 | return getTableHeader().querySelectorAll('th')[n];
25 | }
26 |
27 | // loads all columns
28 | function loadColumns() {
29 | var colNodes = getTableHeader().querySelectorAll('th'),
30 | colNode,
31 | cols = [],
32 | col,
33 | i;
34 |
35 | for (i = 0; i < colNodes.length; i += 1) {
36 | colNode = colNodes[i];
37 | col = {
38 | key: colNode.getAttribute('data-col'),
39 | sortable: !colNode.getAttribute('data-nosort'),
40 | type: colNode.getAttribute('data-type') || 'string'
41 | };
42 | cols.push(col);
43 | if (col.sortable) {
44 | col.defaultDescSort = col.type === 'number';
45 | colNode.innerHTML =
46 | colNode.innerHTML + '';
47 | }
48 | }
49 | return cols;
50 | }
51 | // attaches a data attribute to every tr element with an object
52 | // of data values keyed by column name
53 | function loadRowData(tableRow) {
54 | var tableCols = tableRow.querySelectorAll('td'),
55 | colNode,
56 | col,
57 | data = {},
58 | i,
59 | val;
60 | for (i = 0; i < tableCols.length; i += 1) {
61 | colNode = tableCols[i];
62 | col = cols[i];
63 | val = colNode.getAttribute('data-value');
64 | if (col.type === 'number') {
65 | val = Number(val);
66 | }
67 | data[col.key] = val;
68 | }
69 | return data;
70 | }
71 | // loads all row data
72 | function loadData() {
73 | var rows = getTableBody().querySelectorAll('tr'),
74 | i;
75 |
76 | for (i = 0; i < rows.length; i += 1) {
77 | rows[i].data = loadRowData(rows[i]);
78 | }
79 | }
80 | // sorts the table using the data for the ith column
81 | function sortByIndex(index, desc) {
82 | var key = cols[index].key,
83 | sorter = function(a, b) {
84 | a = a.data[key];
85 | b = b.data[key];
86 | return a < b ? -1 : a > b ? 1 : 0;
87 | },
88 | finalSorter = sorter,
89 | tableBody = document.querySelector('.coverage-summary tbody'),
90 | rowNodes = tableBody.querySelectorAll('tr'),
91 | rows = [],
92 | i;
93 |
94 | if (desc) {
95 | finalSorter = function(a, b) {
96 | return -1 * sorter(a, b);
97 | };
98 | }
99 |
100 | for (i = 0; i < rowNodes.length; i += 1) {
101 | rows.push(rowNodes[i]);
102 | tableBody.removeChild(rowNodes[i]);
103 | }
104 |
105 | rows.sort(finalSorter);
106 |
107 | for (i = 0; i < rows.length; i += 1) {
108 | tableBody.appendChild(rows[i]);
109 | }
110 | }
111 | // removes sort indicators for current column being sorted
112 | function removeSortIndicators() {
113 | var col = getNthColumn(currentSort.index),
114 | cls = col.className;
115 |
116 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
117 | col.className = cls;
118 | }
119 | // adds sort indicators for current column being sorted
120 | function addSortIndicators() {
121 | getNthColumn(currentSort.index).className += currentSort.desc
122 | ? ' sorted-desc'
123 | : ' sorted';
124 | }
125 | // adds event listeners for all sorter widgets
126 | function enableUI() {
127 | var i,
128 | el,
129 | ithSorter = function ithSorter(i) {
130 | var col = cols[i];
131 |
132 | return function() {
133 | var desc = col.defaultDescSort;
134 |
135 | if (currentSort.index === i) {
136 | desc = !currentSort.desc;
137 | }
138 | sortByIndex(i, desc);
139 | removeSortIndicators();
140 | currentSort.index = i;
141 | currentSort.desc = desc;
142 | addSortIndicators();
143 | };
144 | };
145 | for (i = 0; i < cols.length; i += 1) {
146 | if (cols[i].sortable) {
147 | // add the click event handler on the th so users
148 | // dont have to click on those tiny arrows
149 | el = getNthColumn(i).querySelector('.sorter').parentElement;
150 | if (el.addEventListener) {
151 | el.addEventListener('click', ithSorter(i));
152 | } else {
153 | el.attachEvent('onclick', ithSorter(i));
154 | }
155 | }
156 | }
157 | }
158 | // adds sorting functionality to the UI
159 | return function() {
160 | if (!getTable()) {
161 | return;
162 | }
163 | cols = loadColumns();
164 | loadData();
165 | addSortIndicators();
166 | enableUI();
167 | };
168 | })();
169 |
170 | window.addEventListener('load', addSorting);
171 |
--------------------------------------------------------------------------------
/coverage/lcov-report/base.css:
--------------------------------------------------------------------------------
1 | body, html {
2 | margin:0; padding: 0;
3 | height: 100%;
4 | }
5 | body {
6 | font-family: Helvetica Neue, Helvetica, Arial;
7 | font-size: 14px;
8 | color:#333;
9 | }
10 | .small { font-size: 12px; }
11 | *, *:after, *:before {
12 | -webkit-box-sizing:border-box;
13 | -moz-box-sizing:border-box;
14 | box-sizing:border-box;
15 | }
16 | h1 { font-size: 20px; margin: 0;}
17 | h2 { font-size: 14px; }
18 | pre {
19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20 | margin: 0;
21 | padding: 0;
22 | -moz-tab-size: 2;
23 | -o-tab-size: 2;
24 | tab-size: 2;
25 | }
26 | a { color:#0074D9; text-decoration:none; }
27 | a:hover { text-decoration:underline; }
28 | .strong { font-weight: bold; }
29 | .space-top1 { padding: 10px 0 0 0; }
30 | .pad2y { padding: 20px 0; }
31 | .pad1y { padding: 10px 0; }
32 | .pad2x { padding: 0 20px; }
33 | .pad2 { padding: 20px; }
34 | .pad1 { padding: 10px; }
35 | .space-left2 { padding-left:55px; }
36 | .space-right2 { padding-right:20px; }
37 | .center { text-align:center; }
38 | .clearfix { display:block; }
39 | .clearfix:after {
40 | content:'';
41 | display:block;
42 | height:0;
43 | clear:both;
44 | visibility:hidden;
45 | }
46 | .fl { float: left; }
47 | @media only screen and (max-width:640px) {
48 | .col3 { width:100%; max-width:100%; }
49 | .hide-mobile { display:none!important; }
50 | }
51 |
52 | .quiet {
53 | color: #7f7f7f;
54 | color: rgba(0,0,0,0.5);
55 | }
56 | .quiet a { opacity: 0.7; }
57 |
58 | .fraction {
59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60 | font-size: 10px;
61 | color: #555;
62 | background: #E8E8E8;
63 | padding: 4px 5px;
64 | border-radius: 3px;
65 | vertical-align: middle;
66 | }
67 |
68 | div.path a:link, div.path a:visited { color: #333; }
69 | table.coverage {
70 | border-collapse: collapse;
71 | margin: 10px 0 0 0;
72 | padding: 0;
73 | }
74 |
75 | table.coverage td {
76 | margin: 0;
77 | padding: 0;
78 | vertical-align: top;
79 | }
80 | table.coverage td.line-count {
81 | text-align: right;
82 | padding: 0 5px 0 20px;
83 | }
84 | table.coverage td.line-coverage {
85 | text-align: right;
86 | padding-right: 10px;
87 | min-width:20px;
88 | }
89 |
90 | table.coverage td span.cline-any {
91 | display: inline-block;
92 | padding: 0 5px;
93 | width: 100%;
94 | }
95 | .missing-if-branch {
96 | display: inline-block;
97 | margin-right: 5px;
98 | border-radius: 3px;
99 | position: relative;
100 | padding: 0 4px;
101 | background: #333;
102 | color: yellow;
103 | }
104 |
105 | .skip-if-branch {
106 | display: none;
107 | margin-right: 10px;
108 | position: relative;
109 | padding: 0 4px;
110 | background: #ccc;
111 | color: white;
112 | }
113 | .missing-if-branch .typ, .skip-if-branch .typ {
114 | color: inherit !important;
115 | }
116 | .coverage-summary {
117 | border-collapse: collapse;
118 | width: 100%;
119 | }
120 | .coverage-summary tr { border-bottom: 1px solid #bbb; }
121 | .keyline-all { border: 1px solid #ddd; }
122 | .coverage-summary td, .coverage-summary th { padding: 10px; }
123 | .coverage-summary tbody { border: 1px solid #bbb; }
124 | .coverage-summary td { border-right: 1px solid #bbb; }
125 | .coverage-summary td:last-child { border-right: none; }
126 | .coverage-summary th {
127 | text-align: left;
128 | font-weight: normal;
129 | white-space: nowrap;
130 | }
131 | .coverage-summary th.file { border-right: none !important; }
132 | .coverage-summary th.pct { }
133 | .coverage-summary th.pic,
134 | .coverage-summary th.abs,
135 | .coverage-summary td.pct,
136 | .coverage-summary td.abs { text-align: right; }
137 | .coverage-summary td.file { white-space: nowrap; }
138 | .coverage-summary td.pic { min-width: 120px !important; }
139 | .coverage-summary tfoot td { }
140 |
141 | .coverage-summary .sorter {
142 | height: 10px;
143 | width: 7px;
144 | display: inline-block;
145 | margin-left: 0.5em;
146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147 | }
148 | .coverage-summary .sorted .sorter {
149 | background-position: 0 -20px;
150 | }
151 | .coverage-summary .sorted-desc .sorter {
152 | background-position: 0 -10px;
153 | }
154 | .status-line { height: 10px; }
155 | /* yellow */
156 | .cbranch-no { background: yellow !important; color: #111; }
157 | /* dark red */
158 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159 | .low .chart { border:1px solid #C21F39 }
160 | .highlighted,
161 | .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162 | background: #C21F39 !important;
163 | }
164 | /* medium red */
165 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166 | /* light red */
167 | .low, .cline-no { background:#FCE1E5 }
168 | /* light green */
169 | .high, .cline-yes { background:rgb(230,245,208) }
170 | /* medium green */
171 | .cstat-yes { background:rgb(161,215,106) }
172 | /* dark green */
173 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174 | .high .chart { border:1px solid rgb(77,146,33) }
175 | /* dark yellow (gold) */
176 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; }
177 | .medium .chart { border:1px solid #f9cd0b; }
178 | /* light yellow */
179 | .medium { background: #fff4c2; }
180 |
181 | .cstat-skip { background: #ddd; color: #111; }
182 | .fstat-skip { background: #ddd; color: #111 !important; }
183 | .cbranch-skip { background: #ddd !important; color: #111; }
184 |
185 | span.cline-neutral { background: #eaeaea; }
186 |
187 | .coverage-summary td.empty {
188 | opacity: .5;
189 | padding-top: 4px;
190 | padding-bottom: 4px;
191 | line-height: 1;
192 | color: #888;
193 | }
194 |
195 | .cover-fill, .cover-empty {
196 | display:inline-block;
197 | height: 12px;
198 | }
199 | .chart {
200 | line-height: 0;
201 | }
202 | .cover-empty {
203 | background: white;
204 | }
205 | .cover-full {
206 | border-right: none !important;
207 | }
208 | pre.prettyprint {
209 | border: none !important;
210 | padding: 0 !important;
211 | margin: 0 !important;
212 | }
213 | .com { color: #999 !important; }
214 | .ignore-none { color: #999; font-weight: normal; }
215 |
216 | .wrapper {
217 | min-height: 100%;
218 | height: auto !important;
219 | height: 100%;
220 | margin: 0 auto -48px;
221 | }
222 | .footer, .push {
223 | height: 48px;
224 | }
225 |
--------------------------------------------------------------------------------
/dist/js/monitor.0.0.2-beta.js:
--------------------------------------------------------------------------------
1 | !function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="/dist/js/",n(n.s="QfWi")}({"/tra":function(t,e,n){"use strict";function o(){return this.data=[],this}o.prototype={_get(){return this.data},_set(t){this.data.push(t)}},t.exports=o},"20nU":function(t,e,n){"use strict";function o(t){this._extend(this,t)}o.prototype={https:!0,post:!0,url:"/monitor",_extend:(t,e)=>(Object.keys(e).map(n=>{t[n]=e[n]}),t)},t.exports=o},QfWi:function(t,e,n){"use strict";var o,r=(o=n("tjUo"))&&o.__esModule?o:{default:o};!function(t){t.initMonitor||(t.initMonitor=r.default)}(window)},f6yO:function(t,e,n){"use strict";var o=a(n("20nU")),r=n("xziN"),i=a(n("/tra"));function a(t){return t&&t.__esModule?t:{default:t}}function s(t){let e=this,n=new o.default(t);e._config=n,e._config.protocol=window.location.protocol+"//",n.https&&(e._config.protocol="https://");const r=new i.default;e._eventCenter=r,e._initListenJS(),e._initListenAjax()}s.prototype={_initListenJS:function(){const t=this;let e=function(e){(0,r.getJsError)(e,t._config)};window.addEventListener("unhandledrejection",e),t._setEvent({type:"unhandledrejection",func:e});let n=function(e){if(e.cancelable){if(e.filename.indexOf("monitor")>-1)return;(0,r.getJsError)(e,t._config)}else(0,r.geetResourceError)(e,t._config)};window.addEventListener("error",n,!0),t._setEvent({type:"error",func:n})},_initListenAjax:function(){function t(t){var e=new CustomEvent(t,{detail:this});window.dispatchEvent(e)}var e=window.XMLHttpRequest;window.XMLHttpRequest=function(){var n=new e;return n.addEventListener("load",function(){t.call(this,"ajaxLoad")},!1),n.addEventListener("timeout",function(){t.call(this,"ajaxTimeout")},!1),n.addEventListener("readystatechange",function(){t.call(this,"ajaxReadyStateChange")},!1),n},this._startLintenAjax()},_startLintenAjax:function(){const t=this;let e=function(e){!(e.detail.responseURL.indexOf(t._config.url)>-1)&&(0,r.ajaxError)(e,t._config)};window.addEventListener("ajaxTimeout",e),t._setEvent({type:"ajaxTimeout",func:e});let n=function(e){!(e.detail.responseURL.indexOf(t._config.url)>-1)&&(0,r.ajaxError)(e,t._config)};window.addEventListener("ajaxLoad",n),t._setEvent({type:"ajaxLoad",func:n})},_getEvent:function(){return this._eventCenter._get()},_setEvent:function(t){this._eventCenter._set(t)}},t.exports=s},oLOV:function(t,e,n){"use strict";const o={canAjax:function(){return window.XMLHttpRequest&&window.JSON},post:function(t,e,n){var o=new XMLHttpRequest;o.open("post",t,!0),o.setRequestHeader("content-type","application/json;charset=utf-8"),o.setRequestHeader("Accept","application/json"),o.withCredentials=!0,o.timeout=n||3e4,o.onload=function(){1===window.JSON.parse(o.responseText).status&&changeStatus()},o.onreadystatechange=function(){if(4===o.readyState){if(200!==o.status)throw new Error("网络请求错误,请稍后再试~");1===window.JSON.parse(o.responseText).status&&changeStatus()}},o.send(window.JSON.stringify(e))},getWraper:function(t,e,n){(new e)._geWrap()}};t.exports={ajax:o}},"pB/M":function(t,e,n){"use strict";function o(){this.data=this._geWrap()}o.prototype={_geWrap:function(){let t={},e=window.navigator;return t.userAgent=e.userAgent,t.appName=e.appName,t.appVersion=e.appVersion,t.cpuClass=e.cpuClass,t.platform=e.platform,t.product=e.product,t.language=e.language,t.url=window.location.href,t.time=(new Date).getTime(),t},_getIP:function(t){var e=new(window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection)({iceServers:[{urls:"stun:stun01.sipphone.com"},{urls:"stun:stun.ekiga.net"},{urls:"stun:stun.fwdnet.net"},{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun.l.google.com:19302"}]}),n=function(){},o={},r=/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g;function i(e){o[e]||t(e),o[e]=!0}e.createDataChannel(""),e.createOffer().then(function(t){t.sdp.split("\n").forEach(function(t){t.indexOf("candidate")<0||t.match(r).forEach(i)}),e.setLocalDescription(t,n,n)}).catch(function(t){}),e.onicecandidate=function(t){t&&t.candidate&&t.candidate.candidate&&t.candidate.candidate.match(r)&&t.candidate.candidate.match(r).forEach(i)}},_getErrorMessage:function(t,e){let n=this._geWrap();return this._getIP(function(t){n.ip=t}),n.detail={},"ajaxLoad"===t.type?(n.detail.responseURL=t.detail.responseURL,n.detail.status=t.detail.status,n.detail.statusText=t.detail.statusText,n.detail.type="ajaxLoad"):"error"===t.type?(n.detail.message=t.message,n.detail.line=t.lineno,n.detail.filename=t.filename,n.detail.type="error"):e&&(n.detail.src=t.target.src,n.detail.type="resource"),n},_getCallStack(){for(var t="#",e=arguments.callee;e=e.caller;)t=t+""+e.name,0;return t}},t.exports=o},tjUo:function(t,e,n){"use strict";var o=i(n("f6yO")),r=i(n("y11X"));function i(t){return t&&t.__esModule?t:{default:t}}t.exports=function(t,e){const n=new o.default(t);return e(new r.default(n)),n}},xziN:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.geetResourceError=e.getJsError=e.ajaxError=e.getServerError=void 0;var o,r=n("oLOV"),i=(o=n("pB/M"))&&o.__esModule?o:{default:o};e.getServerError=function(){};e.ajaxError=function(t,e){if("ajaxLoad"===t.type&&t.detail.status>=400){let n=(new i.default)._getErrorMessage(t);r.ajax.post(e.protocol+e.url,n,function(){},function(t){console.log(t)})}};e.getJsError=function(t,e){let n=(new i.default)._getErrorMessage(t);r.ajax.post(e.protocol+e.url,n,function(){},function(t){console.log(t)})};e.geetResourceError=function(t,e){let n=(new i.default)._getErrorMessage(t);r.ajax.post(e.protocol+e.url,n,function(){},function(t){console.log(t)})}},y11X:function(t,e,n){"use strict";function o(t){return this._InitMonitor=t,this}o.prototype={_destory:function(){const t=this._InitMonitor._getEvent();for(let e=0;e(Object.keys(e).map(n=>{t[n]=e[n]}),t)},t.exports=o},QfWi:function(t,e,n){"use strict";var o,r=(o=n("tjUo"))&&o.__esModule?o:{default:o};!function(t){t.initMonitor||(t.initMonitor=r.default)}(window)},f6yO:function(t,e,n){"use strict";var o=a(n("20nU")),r=n("xziN"),i=a(n("/tra"));function a(t){return t&&t.__esModule?t:{default:t}}function s(t){let e=this,n=new o.default(t);e._config=n,e._config.protocol=window.location.protocol+"//",n.https&&(e._config.protocol="https://");const r=new i.default;e._eventCenter=r,e._initListenJS(),e._initListenAjax()}s.prototype={_initListenJS:function(){const t=this;let e=function(e){return getError(e,t._config),!0};window.addEventListener("unhandledrejection",e),t._setEvent({type:"unhandledrejection",func:e});let n=function(e){if(e.cancelable){if(e.filename.indexOf("monitor")>-1)return;(0,r.getJsError)(e,t._config)}else(0,r.geetResourceError)(e,t._config)};window.addEventListener("error",n,!0),t._setEvent({type:"error",func:n})},_initListenAjax:function(){function t(t){var e=new CustomEvent(t,{detail:this});window.dispatchEvent(e)}var e=window.XMLHttpRequest;window.XMLHttpRequest=function(){var n=new e;return n.addEventListener("load",function(){t.call(this,"ajaxLoad")},!1),n.addEventListener("timeout",function(){t.call(this,"ajaxTimeout")},!1),n.addEventListener("readystatechange",function(){t.call(this,"ajaxReadyStateChange")},!1),n},this._startLintenAjax()},_startLintenAjax:function(){const t=this;let e=function(e){e.detail.responseURL.indexOf(t._config.url)>-1||(0,r.ajaxError)(e,t._config)};window.addEventListener("ajaxTimeout",e),t._setEvent({type:"ajaxTimeout",func:e});let n=function(e){e.detail.responseURL.indexOf(t._config.url)>-1||(0,r.ajaxError)(e,t._config)};window.addEventListener("ajaxLoad",n),t._setEvent({type:"ajaxLoad",func:n})},_getEvent:function(){return this._eventCenter._get()},_setEvent:function(t){this._eventCenter._set(t)}},t.exports=s},oLOV:function(t,e,n){"use strict";const o={canAjax:function(){return window.XMLHttpRequest&&window.JSON},post:function(t,e,n){var o=new XMLHttpRequest;o.open("post",t,!0),o.setRequestHeader("content-type","application/json;charset=utf-8"),o.setRequestHeader("Accept","application/json"),o.withCredentials=!0,o.timeout=n||3e4,o.onload=function(){1===window.JSON.parse(o.responseText).status&&changeStatus()},o.onreadystatechange=function(){if(4===o.readyState){if(200!==o.status)throw new Error("网络请求错误,请稍后再试~");1===window.JSON.parse(o.responseText).status&&changeStatus()}},o.send(window.JSON.stringify(e))},getWraper:function(t,e,n){(new e)._geWrap()}};t.exports={ajax:o}},"pB/M":function(t,e,n){"use strict";function o(){this.data=this._geWrap()}o.prototype={_geWrap:function(){let t={},e=window.navigator;return t.userAgent=e.userAgent,t.appName=e.appName,t.appVersion=e.appVersion,t.cpuClass=e.cpuClass,t.platform=e.platform,t.product=e.product,t.language=e.language,t.url=window.location.href,t.time=(new Date).getTime(),t},_getIP:function(t){var e=new(window.RTCPeerConnection||window.mozRTCPeerConnection||window.webkitRTCPeerConnection)({iceServers:[{urls:"stun:stun01.sipphone.com"},{urls:"stun:stun.ekiga.net"},{urls:"stun:stun.fwdnet.net"},{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun.l.google.com:19302"}]}),n=function(){},o={},r=/([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/g;function i(e){o[e]||t(e),o[e]=!0}e.createDataChannel(""),e.createOffer().then(function(t){t.sdp.split("\n").forEach(function(t){t.indexOf("candidate")<0||t.match(r).forEach(i)}),e.setLocalDescription(t,n,n)}).catch(function(t){}),e.onicecandidate=function(t){t&&t.candidate&&t.candidate.candidate&&t.candidate.candidate.match(r)&&t.candidate.candidate.match(r).forEach(i)}},_getErrorMessage:function(t,e){let n=this._geWrap();return this._getIP(function(t){n.ip=t}),n.detail={},"ajaxLoad"===t.type?(n.detail.responseURL=t.detail.responseURL,n.detail.status=t.detail.status,n.detail.statusText=t.detail.statusText,n.detail.type="ajaxLoad"):"error"===t.type?(n.detail.message=t.message,n.detail.line=t.lineno,n.detail.filename=t.filename,n.detail.type="error",n.detail.stack=t.error.stack):e&&(n.detail.src=t.target.src,n.detail.type="resource"),n},_getCallStack(){for(var t="#",e=arguments.callee;e=e.caller;)t=t+""+e.name,0;return t}},t.exports=o},tjUo:function(t,e,n){"use strict";var o=i(n("f6yO")),r=i(n("y11X"));function i(t){return t&&t.__esModule?t:{default:t}}t.exports=function(t,e){const n=new o.default(t);return e(new r.default(n)),n}},xziN:function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.geetResourceError=e.getJsError=e.ajaxError=e.getServerError=void 0;var o,r=n("oLOV"),i=(o=n("pB/M"))&&o.__esModule?o:{default:o};e.getServerError=function(){};e.ajaxError=function(t,e){if("ajaxLoad"===t.type&&t.detail.status>300){let n=(new i.default)._getErrorMessage(t);r.ajax.post(e.protocol+e.url,n,function(){},function(t){console.log(t)})}};e.getJsError=function(t,e){let n=(new i.default)._getErrorMessage(t);r.ajax.post(e.protocol+e.url,n,function(){},function(t){console.log(t)})};e.geetResourceError=function(t,e){let n=r.ajax.getWraper(t,i.default,!0);r.ajax.post(e.protocol+e.url,n,function(){},function(t){console.log(t)})}},y11X:function(t,e,n){"use strict";function o(t){return this._InitMonitor=t,this}o.prototype={_destory:function(){const t=this._InitMonitor._getEvent();for(let e=0;e
2 |
3 |
4 | Code coverage report for All files
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
19 | All files
20 |
21 |
22 |
23 | 15.38%
24 | Statements
25 | 4/26
26 |
27 |
28 | 0%
29 | Branches
30 | 0/12
31 |
32 |
33 | 14.29%
34 | Functions
35 | 1/7
36 |
37 |
38 | 15.38%
39 | Lines
40 | 4/26
41 |
42 |
43 |
44 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | | File |
53 | |
54 | Statements |
55 | |
56 | Branches |
57 | |
58 | Functions |
59 | |
60 | Lines |
61 | |
62 |
63 |
64 |
65 | | ajax.js |
66 | |
67 | 0% |
68 | 0/22 |
69 | 0% |
70 | 0/12 |
71 | 0% |
72 | 0/5 |
73 | 0% |
74 | 0/22 |
75 |
76 |
77 |
78 | | config.js |
79 | |
80 | 0% |
81 | 0/0 |
82 | 0% |
83 | 0/0 |
84 | 0% |
85 | 0/1 |
86 | 0% |
87 | 0/0 |
88 |
89 |
90 |
91 | | error.js |
92 | |
93 | 0% |
94 | 0/0 |
95 | 0% |
96 | 0/0 |
97 | 0% |
98 | 0/0 |
99 | 0% |
100 | 0/0 |
101 |
102 |
103 |
104 | | index.js |
105 | |
106 | 100% |
107 | 4/4 |
108 | 100% |
109 | 0/0 |
110 | 100% |
111 | 1/1 |
112 | 100% |
113 | 4/4 |
114 |
115 |
116 |
117 | | stateChange.js |
118 | |
119 | 0% |
120 | 0/0 |
121 | 0% |
122 | 0/0 |
123 | 0% |
124 | 0/0 |
125 | 0% |
126 | 0/0 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
137 |
138 |
139 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/coverage/lcov-report/ajax.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Code coverage report for ajax.js
5 |
6 |
7 |
8 |
9 |
14 |
15 |
16 |
17 |
18 |
21 |
22 |
23 | 0%
24 | Statements
25 | 0/22
26 |
27 |
28 | 0%
29 | Branches
30 | 0/12
31 |
32 |
33 | 0%
34 | Functions
35 | 0/5
36 |
37 |
38 | 0%
39 | Lines
40 | 0/22
41 |
42 |
43 |
44 | Press n or j to go to the next uncovered block, b, p or k for the previous block.
45 |
46 |
47 |
48 |
49 | | 1
50 | 2
51 | 3
52 | 4
53 | 5
54 | 6
55 | 7
56 | 8
57 | 9
58 | 10
59 | 11
60 | 12
61 | 13
62 | 14
63 | 15
64 | 16
65 | 17
66 | 18
67 | 19
68 | 20
69 | 21
70 | 22
71 | 23
72 | 24
73 | 25
74 | 26
75 | 27
76 | 28
77 | 29
78 | 30
79 | 31
80 | 32
81 | 33
82 | 34
83 | 35
84 | 36
85 | 37
86 | 38
87 | 39
88 | 40
89 | 41
90 | 42
91 | 43
92 | 44
93 | 45 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 | | /**
138 | * @file ajax request file
139 | * @author JYkid
140 | * @version 0.0.1-beta
141 | */
142 |
143 | const ajax = (function() {
144 | return {
145 | canAjax: function() {
146 | return (window.XMLHttpRequest && window.JSON);
147 | },
148 | post: function(config, url, data, timeout) {
149 | var xhr = new XMLHttpRequest();
150 | xhr.open(config.type, url, true);
151 | xhr.setRequestHeader("content-type", "application/json;charset=utf-8");
152 | xhr.setRequestHeader("Accept", "application/json");
153 | xhr.withCredentials = true;
154 | xhr.timeout = timeout || 30000;
155 | xhr.onload = function () {
156 | var result = window.JSON.parse(xhr.responseText);
157 | if (result.status === 1) {
158 | changeStatus()
159 | }
160 | };
161 | xhr.onreadystatechange = function () {
162 | if (xhr.readyState === 4) {
163 | if (xhr.status === 200) {
164 | var result = window.JSON.parse(xhr.responseText);
165 | if (result.status === 1) {
166 | changeStatus()
167 | }
168 | } else {
169 | throw new Error("网络请求错误,请稍后再试~");
170 | }
171 | }
172 | };
173 | xhr.send(window.JSON.stringify(data));
174 | }
175 | }
176 | })();
177 |
178 | module.exports = {
179 | ajax,
180 | }
181 | |
182 |
183 |
184 |
185 |
189 |
190 |
191 |
198 |
199 |
200 |
201 |
202 |
--------------------------------------------------------------------------------
/coverage/lcov-report/prettify.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^