├── .babelrc
├── .electron-vue
├── build.js
├── dev-client.js
├── dev-runner.js
├── webpack.main.config.js
├── webpack.renderer.config.js
└── webpack.web.config.js
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmrc
├── .travis.yml
├── LICENSE
├── README.md
├── appveyor.yml
├── build
└── icons
│ ├── 256x256.png
│ ├── icon.icns
│ └── icon.ico
├── dist
├── electron
│ └── .gitkeep
└── web
│ └── .gitkeep
├── doc
├── 1.png
├── 10.png
├── 11.png
├── 12.png
├── 13.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 6.png
├── 7.png
├── 8.png
├── 9.png
└── menu.png
├── package.json
├── src
├── index.ejs
├── main
│ ├── event
│ │ └── portscan.js
│ ├── ftp
│ │ └── index.js
│ ├── http
│ │ └── index.js
│ ├── index.dev.js
│ ├── index.js
│ └── proxy
│ │ └── index.js
└── renderer
│ ├── App.vue
│ ├── assets
│ ├── .gitkeep
│ ├── global.css
│ └── logo.png
│ ├── components
│ ├── Deprecated
│ │ ├── ColorCard.vue
│ │ ├── Doc.vue
│ │ ├── Http.vue
│ │ └── SSHEmulator.vue
│ ├── LandingPage.vue
│ ├── LandingPage
│ │ └── SystemInformation.vue
│ ├── LeftMenu.vue
│ ├── MainView.vue
│ └── View
│ │ ├── AnyProxy.vue
│ │ ├── CodeSnippet.vue
│ │ ├── Exec.vue
│ │ ├── Settings.vue
│ │ ├── Str
│ │ ├── StrBejson.vue
│ │ ├── StrDiff.vue
│ │ ├── StrEncode.vue
│ │ └── StrQrcode.vue
│ │ ├── Sys
│ │ ├── FTPServer.vue
│ │ ├── HTTPServer.vue
│ │ ├── SysPortScan.vue
│ │ └── SysProcess.vue
│ │ └── Tail.vue
│ ├── datastore.js
│ ├── main.js
│ ├── router
│ └── index.js
│ ├── store
│ ├── index.js
│ └── modules
│ │ ├── Counter.js
│ │ ├── Exec.js
│ │ ├── Nav.js
│ │ ├── Settings.js
│ │ ├── View.js
│ │ └── index.js
│ └── utils
│ ├── bejson.js
│ ├── common.js
│ ├── difflib.js
│ ├── diffview.js
│ └── language.json
├── static
├── .gitkeep
└── ports.json
└── test
├── .eslintrc
├── e2e
├── index.js
├── specs
│ └── Launch.spec.js
└── utils.js
└── unit
├── index.js
├── karma.conf.js
└── specs
└── LandingPage.spec.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "comments": false,
3 | "env": {
4 | "test": {
5 | "presets": [
6 | ["env", {
7 | "targets": { "node": 7 }
8 | }],
9 | "stage-0"
10 | ],
11 | "plugins": ["istanbul"]
12 | },
13 | "main": {
14 | "presets": [
15 | ["env", {
16 | "targets": { "node": 7 }
17 | }],
18 | "stage-0"
19 | ]
20 | },
21 | "renderer": {
22 | "presets": [
23 | ["env", {
24 | "modules": false
25 | }],
26 | "stage-0"
27 | ]
28 | },
29 | "web": {
30 | "presets": [
31 | ["env", {
32 | "modules": false
33 | }],
34 | "stage-0"
35 | ]
36 | }
37 | },
38 | "plugins": ["transform-runtime"]
39 | }
40 |
--------------------------------------------------------------------------------
/.electron-vue/build.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.NODE_ENV = 'production'
4 |
5 | const { say } = require('cfonts')
6 | const chalk = require('chalk')
7 | const del = require('del')
8 | const { spawn } = require('child_process')
9 | const webpack = require('webpack')
10 | const Multispinner = require('multispinner')
11 |
12 |
13 | const mainConfig = require('./webpack.main.config')
14 | const rendererConfig = require('./webpack.renderer.config')
15 | const webConfig = require('./webpack.web.config')
16 |
17 | const doneLog = chalk.bgGreen.white(' DONE ') + ' '
18 | const errorLog = chalk.bgRed.white(' ERROR ') + ' '
19 | const okayLog = chalk.bgBlue.white(' OKAY ') + ' '
20 | const isCI = process.env.CI || false
21 |
22 | if (process.env.BUILD_TARGET === 'clean') clean()
23 | else if (process.env.BUILD_TARGET === 'web') web()
24 | else build()
25 |
26 | function clean () {
27 | del.sync(['build/*', '!build/icons', '!build/icons/icon.*'])
28 | console.log(`\n${doneLog}\n`)
29 | process.exit()
30 | }
31 |
32 | function build () {
33 | greeting()
34 |
35 | del.sync(['dist/electron/*', '!.gitkeep'])
36 |
37 | const tasks = ['main', 'renderer']
38 | const m = new Multispinner(tasks, {
39 | preText: 'building',
40 | postText: 'process'
41 | })
42 |
43 | let results = ''
44 |
45 | m.on('success', () => {
46 | process.stdout.write('\x1B[2J\x1B[0f')
47 | console.log(`\n\n${results}`)
48 | console.log(`${okayLog}take it away ${chalk.yellow('`electron-builder`')}\n`)
49 | process.exit()
50 | })
51 |
52 | pack(mainConfig).then(result => {
53 | results += result + '\n\n'
54 | m.success('main')
55 | }).catch(err => {
56 | m.error('main')
57 | console.log(`\n ${errorLog}failed to build main process`)
58 | console.error(`\n${err}\n`)
59 | process.exit(1)
60 | })
61 |
62 | pack(rendererConfig).then(result => {
63 | results += result + '\n\n'
64 | m.success('renderer')
65 | }).catch(err => {
66 | m.error('renderer')
67 | console.log(`\n ${errorLog}failed to build renderer process`)
68 | console.error(`\n${err}\n`)
69 | process.exit(1)
70 | })
71 | }
72 |
73 | function pack (config) {
74 | return new Promise((resolve, reject) => {
75 | webpack(config, (err, stats) => {
76 | if (err) reject(err.stack || err)
77 | else if (stats.hasErrors()) {
78 | let err = ''
79 |
80 | stats.toString({
81 | chunks: false,
82 | colors: true
83 | })
84 | .split(/\r?\n/)
85 | .forEach(line => {
86 | err += ` ${line}\n`
87 | })
88 |
89 | reject(err)
90 | } else {
91 | resolve(stats.toString({
92 | chunks: false,
93 | colors: true
94 | }))
95 | }
96 | })
97 | })
98 | }
99 |
100 | function web () {
101 | del.sync(['dist/web/*', '!.gitkeep'])
102 | webpack(webConfig, (err, stats) => {
103 | if (err || stats.hasErrors()) console.log(err)
104 |
105 | console.log(stats.toString({
106 | chunks: false,
107 | colors: true
108 | }))
109 |
110 | process.exit()
111 | })
112 | }
113 |
114 | function greeting () {
115 | const cols = process.stdout.columns
116 | let text = ''
117 |
118 | if (cols > 85) text = 'lets-build'
119 | else if (cols > 60) text = 'lets-|build'
120 | else text = false
121 |
122 | if (text && !isCI) {
123 | say(text, {
124 | colors: ['yellow'],
125 | font: 'simple3d',
126 | space: false
127 | })
128 | } else console.log(chalk.yellow.bold('\n lets-build'))
129 | console.log()
130 | }
131 |
--------------------------------------------------------------------------------
/.electron-vue/dev-client.js:
--------------------------------------------------------------------------------
1 | const hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
2 |
3 | hotClient.subscribe(event => {
4 | /**
5 | * Reload browser when HTMLWebpackPlugin emits a new index.html
6 | *
7 | * Currently disabled until jantimon/html-webpack-plugin#680 is resolved.
8 | * https://github.com/SimulatedGREG/electron-vue/issues/437
9 | * https://github.com/jantimon/html-webpack-plugin/issues/680
10 | */
11 | // if (event.action === 'reload') {
12 | // window.location.reload()
13 | // }
14 |
15 | /**
16 | * Notify `mainWindow` when `main` process is compiling,
17 | * giving notice for an expected reload of the `electron` process
18 | */
19 | if (event.action === 'compiling') {
20 | document.body.innerHTML += `
21 |
34 |
35 |
36 | Compiling Main Process...
37 |
38 | `
39 | }
40 | })
41 |
--------------------------------------------------------------------------------
/.electron-vue/dev-runner.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const chalk = require('chalk')
4 | const electron = require('electron')
5 | const path = require('path')
6 | const { say } = require('cfonts')
7 | const { spawn } = require('child_process')
8 | const webpack = require('webpack')
9 | const WebpackDevServer = require('webpack-dev-server')
10 | const webpackHotMiddleware = require('webpack-hot-middleware')
11 |
12 | const mainConfig = require('./webpack.main.config')
13 | const rendererConfig = require('./webpack.renderer.config')
14 |
15 | let electronProcess = null
16 | let manualRestart = false
17 | let hotMiddleware
18 |
19 | function logStats (proc, data) {
20 | let log = ''
21 |
22 | log += chalk.yellow.bold(`┏ ${proc} Process ${new Array((19 - proc.length) + 1).join('-')}`)
23 | log += '\n\n'
24 |
25 | if (typeof data === 'object') {
26 | data.toString({
27 | colors: true,
28 | chunks: false
29 | }).split(/\r?\n/).forEach(line => {
30 | log += ' ' + line + '\n'
31 | })
32 | } else {
33 | log += ` ${data}\n`
34 | }
35 |
36 | log += '\n' + chalk.yellow.bold(`┗ ${new Array(28 + 1).join('-')}`) + '\n'
37 |
38 | console.log(log)
39 | }
40 |
41 | function startRenderer () {
42 | return new Promise((resolve, reject) => {
43 | rendererConfig.entry.renderer = [path.join(__dirname, 'dev-client')].concat(rendererConfig.entry.renderer)
44 |
45 | const compiler = webpack(rendererConfig)
46 | hotMiddleware = webpackHotMiddleware(compiler, {
47 | log: false,
48 | heartbeat: 2500
49 | })
50 |
51 | compiler.plugin('compilation', compilation => {
52 | compilation.plugin('html-webpack-plugin-after-emit', (data, cb) => {
53 | hotMiddleware.publish({ action: 'reload' })
54 | cb()
55 | })
56 | })
57 |
58 | compiler.plugin('done', stats => {
59 | logStats('Renderer', stats)
60 | })
61 |
62 | const server = new WebpackDevServer(
63 | compiler,
64 | {
65 | contentBase: path.join(__dirname, '../'),
66 | quiet: true,
67 | before (app, ctx) {
68 | app.use(hotMiddleware)
69 | ctx.middleware.waitUntilValid(() => {
70 | resolve()
71 | })
72 | }
73 | }
74 | )
75 |
76 | server.listen(9080)
77 | })
78 | }
79 |
80 | function startMain () {
81 | return new Promise((resolve, reject) => {
82 | mainConfig.entry.main = [path.join(__dirname, '../src/main/index.dev.js')].concat(mainConfig.entry.main)
83 |
84 | const compiler = webpack(mainConfig)
85 |
86 | compiler.plugin('watch-run', (compilation, done) => {
87 | logStats('Main', chalk.white.bold('compiling...'))
88 | hotMiddleware.publish({ action: 'compiling' })
89 | done()
90 | })
91 |
92 | compiler.watch({}, (err, stats) => {
93 | if (err) {
94 | console.log(err)
95 | return
96 | }
97 |
98 | logStats('Main', stats)
99 |
100 | if (electronProcess && electronProcess.kill) {
101 | manualRestart = true
102 | process.kill(electronProcess.pid)
103 | electronProcess = null
104 | startElectron()
105 |
106 | setTimeout(() => {
107 | manualRestart = false
108 | }, 5000)
109 | }
110 |
111 | resolve()
112 | })
113 | })
114 | }
115 |
116 | function startElectron () {
117 | electronProcess = spawn(electron, ['--inspect=5858', path.join(__dirname, '../dist/electron/main.js')])
118 |
119 | electronProcess.stdout.on('data', data => {
120 | electronLog(data, 'blue')
121 | })
122 | electronProcess.stderr.on('data', data => {
123 | electronLog(data, 'red')
124 | })
125 |
126 | electronProcess.on('close', () => {
127 | if (!manualRestart) process.exit()
128 | })
129 | }
130 |
131 | function electronLog (data, color) {
132 | let log = ''
133 | data = data.toString().split(/\r?\n/)
134 | data.forEach(line => {
135 | log += ` ${line}\n`
136 | })
137 | if (/[0-9A-z]+/.test(log)) {
138 | console.log(
139 | chalk[color].bold('┏ Electron -------------------') +
140 | '\n\n' +
141 | log +
142 | chalk[color].bold('┗ ----------------------------') +
143 | '\n'
144 | )
145 | }
146 | }
147 |
148 | function greeting () {
149 | const cols = process.stdout.columns
150 | let text = ''
151 |
152 | if (cols > 104) text = 'electron-vue'
153 | else if (cols > 76) text = 'electron-|vue'
154 | else text = false
155 |
156 | if (text) {
157 | say(text, {
158 | colors: ['yellow'],
159 | font: 'simple3d',
160 | space: false
161 | })
162 | } else console.log(chalk.yellow.bold('\n electron-vue'))
163 | console.log(chalk.blue(' getting ready...') + '\n')
164 | }
165 |
166 | function init () {
167 | greeting()
168 |
169 | Promise.all([startRenderer(), startMain()])
170 | .then(() => {
171 | startElectron()
172 | })
173 | .catch(err => {
174 | console.error(err)
175 | })
176 | }
177 |
178 | init()
179 |
--------------------------------------------------------------------------------
/.electron-vue/webpack.main.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.BABEL_ENV = 'main'
4 |
5 | const path = require('path')
6 | const { dependencies } = require('../package.json')
7 | const webpack = require('webpack')
8 |
9 | const BabiliWebpackPlugin = require('babili-webpack-plugin')
10 |
11 | let mainConfig = {
12 | entry: {
13 | main: path.join(__dirname, '../src/main/index.js')
14 | },
15 | externals: [
16 | ...Object.keys(dependencies || {})
17 | ],
18 | module: {
19 | rules: [
20 | {
21 | test: /\.(js)$/,
22 | enforce: 'pre',
23 | exclude: /node_modules/,
24 | use: {
25 | loader: 'eslint-loader',
26 | options: {
27 | formatter: require('eslint-friendly-formatter')
28 | }
29 | }
30 | },
31 | {
32 | test: /\.js$/,
33 | use: 'babel-loader',
34 | exclude: /node_modules/
35 | },
36 | {
37 | test: /\.node$/,
38 | use: 'node-loader'
39 | }
40 | ]
41 | },
42 | node: {
43 | __dirname: process.env.NODE_ENV !== 'production',
44 | __filename: process.env.NODE_ENV !== 'production'
45 | },
46 | output: {
47 | filename: '[name].js',
48 | libraryTarget: 'commonjs2',
49 | path: path.join(__dirname, '../dist/electron')
50 | },
51 | plugins: [
52 | new webpack.NoEmitOnErrorsPlugin()
53 | ],
54 | resolve: {
55 | extensions: ['.js', '.json', '.node']
56 | },
57 | target: 'electron-main'
58 | }
59 |
60 | /**
61 | * Adjust mainConfig for development settings
62 | */
63 | if (process.env.NODE_ENV !== 'production') {
64 | mainConfig.plugins.push(
65 | new webpack.DefinePlugin({
66 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
67 | })
68 | )
69 | }
70 |
71 | /**
72 | * Adjust mainConfig for production settings
73 | */
74 | if (process.env.NODE_ENV === 'production') {
75 | mainConfig.plugins.push(
76 | new BabiliWebpackPlugin(),
77 | new webpack.DefinePlugin({
78 | 'process.env.NODE_ENV': '"production"'
79 | })
80 | )
81 | }
82 |
83 | module.exports = mainConfig
84 |
--------------------------------------------------------------------------------
/.electron-vue/webpack.renderer.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.BABEL_ENV = 'renderer'
4 |
5 | const path = require('path')
6 | const { dependencies } = require('../package.json')
7 | const webpack = require('webpack')
8 |
9 | const BabiliWebpackPlugin = require('babili-webpack-plugin')
10 | const CopyWebpackPlugin = require('copy-webpack-plugin')
11 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
12 | const HtmlWebpackPlugin = require('html-webpack-plugin')
13 |
14 | /**
15 | * List of node_modules to include in webpack bundle
16 | *
17 | * Required for specific packages like Vue UI libraries
18 | * that provide pure *.vue files that need compiling
19 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals
20 | */
21 | let whiteListedModules = ['vue']
22 |
23 | let rendererConfig = {
24 | devtool: '#cheap-module-eval-source-map',
25 | entry: {
26 | renderer: path.join(__dirname, '../src/renderer/main.js')
27 | },
28 | externals: [
29 | ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d))
30 | ],
31 | module: {
32 | rules: [
33 | {
34 | test: /\.(js|vue)$/,
35 | enforce: 'pre',
36 | exclude: /node_modules/,
37 | use: {
38 | loader: 'eslint-loader',
39 | options: {
40 | formatter: require('eslint-friendly-formatter')
41 | }
42 | }
43 | },
44 | {
45 | test: /\.css$/,
46 | use: ExtractTextPlugin.extract({
47 | fallback: 'style-loader',
48 | use: 'css-loader'
49 | })
50 | },
51 | {
52 | test: /\.html$/,
53 | use: 'vue-html-loader'
54 | },
55 | {
56 | test: /\.js$/,
57 | use: 'babel-loader',
58 | exclude: /node_modules/
59 | },
60 | {
61 | test: /\.node$/,
62 | use: 'node-loader'
63 | },
64 | {
65 | test: /\.vue$/,
66 | use: {
67 | loader: 'vue-loader',
68 | options: {
69 | extractCSS: process.env.NODE_ENV === 'production',
70 | loaders: {
71 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
72 | scss: 'vue-style-loader!css-loader!sass-loader'
73 | }
74 | }
75 | }
76 | },
77 | {
78 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
79 | use: {
80 | loader: 'url-loader',
81 | query: {
82 | limit: 10000,
83 | name: 'imgs/[name]--[folder].[ext]'
84 | }
85 | }
86 | },
87 | {
88 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
89 | loader: 'url-loader',
90 | options: {
91 | limit: 10000,
92 | name: 'media/[name]--[folder].[ext]'
93 | }
94 | },
95 | {
96 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
97 | use: {
98 | loader: 'url-loader',
99 | query: {
100 | limit: 10000,
101 | name: 'fonts/[name]--[folder].[ext]'
102 | }
103 | }
104 | }
105 | ]
106 | },
107 | node: {
108 | __dirname: process.env.NODE_ENV !== 'production',
109 | __filename: process.env.NODE_ENV !== 'production'
110 | },
111 | plugins: [
112 | new ExtractTextPlugin('styles.css'),
113 | new HtmlWebpackPlugin({
114 | filename: 'index.html',
115 | template: path.resolve(__dirname, '../src/index.ejs'),
116 | minify: {
117 | collapseWhitespace: true,
118 | removeAttributeQuotes: true,
119 | removeComments: true
120 | },
121 | nodeModules: process.env.NODE_ENV !== 'production'
122 | ? path.resolve(__dirname, '../node_modules')
123 | : false
124 | }),
125 | new webpack.HotModuleReplacementPlugin(),
126 | new webpack.NoEmitOnErrorsPlugin()
127 | ],
128 | output: {
129 | filename: '[name].js',
130 | libraryTarget: 'commonjs2',
131 | path: path.join(__dirname, '../dist/electron')
132 | },
133 | resolve: {
134 | alias: {
135 | '@': path.join(__dirname, '../src/renderer'),
136 | 'vue$': 'vue/dist/vue.esm.js'
137 | },
138 | extensions: ['.js', '.vue', '.json', '.css', '.node']
139 | },
140 | target: 'electron-renderer'
141 | }
142 |
143 | /**
144 | * Adjust rendererConfig for development settings
145 | */
146 | if (process.env.NODE_ENV !== 'production') {
147 | rendererConfig.plugins.push(
148 | new webpack.DefinePlugin({
149 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"`
150 | })
151 | )
152 | }
153 |
154 | /**
155 | * Adjust rendererConfig for production settings
156 | */
157 | if (process.env.NODE_ENV === 'production') {
158 | rendererConfig.devtool = ''
159 |
160 | rendererConfig.plugins.push(
161 | new BabiliWebpackPlugin(),
162 | new CopyWebpackPlugin([
163 | {
164 | from: path.join(__dirname, '../static'),
165 | to: path.join(__dirname, '../dist/electron/static'),
166 | ignore: ['.*']
167 | }
168 | ]),
169 | new webpack.DefinePlugin({
170 | 'process.env.NODE_ENV': '"production"'
171 | }),
172 | new webpack.LoaderOptionsPlugin({
173 | minimize: true
174 | })
175 | )
176 | }
177 |
178 | module.exports = rendererConfig
179 |
--------------------------------------------------------------------------------
/.electron-vue/webpack.web.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | process.env.BABEL_ENV = 'web'
4 |
5 | const path = require('path')
6 | const webpack = require('webpack')
7 |
8 | const BabiliWebpackPlugin = require('babili-webpack-plugin')
9 | const CopyWebpackPlugin = require('copy-webpack-plugin')
10 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
11 | const HtmlWebpackPlugin = require('html-webpack-plugin')
12 |
13 | let webConfig = {
14 | devtool: '#cheap-module-eval-source-map',
15 | entry: {
16 | web: path.join(__dirname, '../src/renderer/main.js')
17 | },
18 | module: {
19 | rules: [
20 | {
21 | test: /\.(js|vue)$/,
22 | enforce: 'pre',
23 | exclude: /node_modules/,
24 | use: {
25 | loader: 'eslint-loader',
26 | options: {
27 | formatter: require('eslint-friendly-formatter')
28 | }
29 | }
30 | },
31 | {
32 | test: /\.css$/,
33 | use: ExtractTextPlugin.extract({
34 | fallback: 'style-loader',
35 | use: 'css-loader'
36 | })
37 | },
38 | {
39 | test: /\.html$/,
40 | use: 'vue-html-loader'
41 | },
42 | {
43 | test: /\.js$/,
44 | use: 'babel-loader',
45 | include: [ path.resolve(__dirname, '../src/renderer') ],
46 | exclude: /node_modules/
47 | },
48 | {
49 | test: /\.vue$/,
50 | use: {
51 | loader: 'vue-loader',
52 | options: {
53 | extractCSS: true,
54 | loaders: {
55 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1',
56 | scss: 'vue-style-loader!css-loader!sass-loader'
57 | }
58 | }
59 | }
60 | },
61 | {
62 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
63 | use: {
64 | loader: 'url-loader',
65 | query: {
66 | limit: 10000,
67 | name: 'imgs/[name].[ext]'
68 | }
69 | }
70 | },
71 | {
72 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
73 | use: {
74 | loader: 'url-loader',
75 | query: {
76 | limit: 10000,
77 | name: 'fonts/[name].[ext]'
78 | }
79 | }
80 | }
81 | ]
82 | },
83 | plugins: [
84 | new ExtractTextPlugin('styles.css'),
85 | new HtmlWebpackPlugin({
86 | filename: 'index.html',
87 | template: path.resolve(__dirname, '../src/index.ejs'),
88 | minify: {
89 | collapseWhitespace: true,
90 | removeAttributeQuotes: true,
91 | removeComments: true
92 | },
93 | nodeModules: false
94 | }),
95 | new webpack.DefinePlugin({
96 | 'process.env.IS_WEB': 'true'
97 | }),
98 | new webpack.HotModuleReplacementPlugin(),
99 | new webpack.NoEmitOnErrorsPlugin()
100 | ],
101 | output: {
102 | filename: '[name].js',
103 | path: path.join(__dirname, '../dist/web')
104 | },
105 | resolve: {
106 | alias: {
107 | '@': path.join(__dirname, '../src/renderer'),
108 | 'vue$': 'vue/dist/vue.esm.js'
109 | },
110 | extensions: ['.js', '.vue', '.json', '.css']
111 | },
112 | target: 'web'
113 | }
114 |
115 | /**
116 | * Adjust webConfig for production settings
117 | */
118 | if (process.env.NODE_ENV === 'production') {
119 | webConfig.devtool = ''
120 |
121 | webConfig.plugins.push(
122 | new BabiliWebpackPlugin(),
123 | new CopyWebpackPlugin([
124 | {
125 | from: path.join(__dirname, '../static'),
126 | to: path.join(__dirname, '../dist/web/static'),
127 | ignore: ['.*']
128 | }
129 | ]),
130 | new webpack.DefinePlugin({
131 | 'process.env.NODE_ENV': '"production"'
132 | }),
133 | new webpack.LoaderOptionsPlugin({
134 | minimize: true
135 | })
136 | )
137 | }
138 |
139 | module.exports = webConfig
140 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | test/unit/coverage/**
2 | test/unit/*.js
3 | test/e2e/*.js
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | parserOptions: {
5 | sourceType: 'module'
6 | },
7 | env: {
8 | browser: true,
9 | node: true
10 | },
11 | extends: 'standard',
12 | globals: {
13 | __static: true
14 | },
15 | plugins: [
16 | 'html'
17 | ],
18 | 'rules': {
19 | // allow paren-less arrow functions
20 | 'arrow-parens': 0,
21 | // allow async-await
22 | 'generator-star-spacing': 0,
23 | // allow debugger during development
24 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | dist/electron/*
3 | dist/web/*
4 | build/*
5 | !build/icons
6 | coverage
7 | node_modules/
8 | npm-debug.log
9 | npm-debug.log.*
10 | thumbs.db
11 | !.gitkeep
12 | package-lock.json
13 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npm.taobao.org
2 | chromedriver_cdnurl=http://npm.taobao.org/mirrors/chromedriver/
3 | selenium_cdnurl=http://npm.taobao.org/mirrorss/selenium/
4 | phantomjs_cdnurl=http://npm.taobao.org/mirrors/phantomjs/
5 | electron_mirror=https://npm.taobao.org/mirrors/electron/
6 | disturl=http://npm.taobao.org/mirrors/node
7 | sass_binary_site=https://cdn.npm.taobao.org/dist/node-sass/
8 | operadriver_cdnurl=https://cdn.npm.taobao.org/dist/operadriver/
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | # Commented sections below can be used to run tests on the CI server
2 | # https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
3 | osx_image: xcode8.3
4 | sudo: required
5 | dist: trusty
6 | language: c
7 | matrix:
8 | include:
9 | - os: osx
10 | - os: linux
11 | env: CC=clang CXX=clang++ npm_config_clang=1
12 | compiler: clang
13 | cache:
14 | directories:
15 | - node_modules
16 | - "$HOME/.electron"
17 | - "$HOME/.cache"
18 | addons:
19 | apt:
20 | packages:
21 | - libgnome-keyring-dev
22 | - icnsutils
23 | #- xvfb
24 | before_install:
25 | - mkdir -p /tmp/git-lfs && curl -L https://github.com/github/git-lfs/releases/download/v1.2.1/git-lfs-$([
26 | "$TRAVIS_OS_NAME" == "linux" ] && echo "linux" || echo "darwin")-amd64-1.2.1.tar.gz
27 | | tar -xz -C /tmp/git-lfs --strip-components 1 && /tmp/git-lfs/git-lfs pull
28 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install --no-install-recommends -y icnsutils graphicsmagick xz-utils; fi
29 | install:
30 | #- export DISPLAY=':99.0'
31 | #- Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
32 | - nvm install 7
33 | - curl -o- -L https://yarnpkg.com/install.sh | bash
34 | - source ~/.bashrc
35 | - npm install -g xvfb-maybe
36 | - yarn
37 | script:
38 | #- xvfb-maybe node_modules/.bin/karma start test/unit/karma.conf.js
39 | #- yarn run pack && xvfb-maybe node_modules/.bin/mocha test/e2e
40 | - yarn run build
41 | branches:
42 | only:
43 | - master
44 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 moonrailgun
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # devbox
2 |
3 | > 一款集成程序员常用软件的工具箱应用
4 |
5 | [](https://github.com/moonrailgun/devbox/releases)
6 | [](https://github.com/moonrailgun/devbox/releases)
7 | [](https://ci.appveyor.com/project/moonrailgun/devbox)
8 |
9 | [稳定版下载](https://github.com/moonrailgun/devbox/releases)
10 |
11 | ## 功能模块
12 |
13 | - [x] [代码片段](#代码片段)
14 | - [x] [命令脚本](#命令脚本)
15 | - [x] [日志监控](#日志监控)
16 | - [x] [代理抓包](#代理抓包)
17 | - 常用工具
18 | - 文本处理
19 | - [x] [加密解密](#加密解密)
20 | - [x] [文本比较](#文本比较)
21 | - [x] [二维码生成](#二维码生成)
22 | - [x] [JSON校验](#JSON校验)
23 | - 系统相关
24 | - [x] [系统进程](#系统进程)
25 | - [x] [端口扫描](#端口扫描)
26 | - [x] [HTTP服务](#HTTP服务)
27 | - [x] [FTP服务](#FTP服务)
28 | - [x] 系统设置
29 |
30 | 
31 |
32 | ## 编译步骤
33 |
34 | ``` bash
35 | # install dependencies
36 | npm install
37 |
38 | # serve with hot reload at localhost:9080
39 | npm run dev
40 |
41 | # build electron application for production
42 | npm run build
43 | # or npm run build:dir
44 |
45 | # run unit & end-to-end tests
46 | npm test
47 |
48 |
49 | # lint all JS/Vue component files in `src/`
50 | npm run lint
51 |
52 | ```
53 |
54 | ## 功能描述
55 |
56 | #### 代码片段
57 |
58 | 一个代码片段的管理应用部件, 用户创建、编辑、保存、管理用户的一些常用代码片段。
59 |
60 | - [x] 片段命名
61 | - [x] 片段选择语言与自定义语言
62 | - [x] 片段预览
63 | - [x] 片段编辑
64 | - [x] 片段保存
65 | - [x] 片段添加到剪切板
66 |
67 | 
68 | 
69 |
70 | #### 命令脚本
71 |
72 | 一个管理常用命令的部件,支持自定义变量
73 | 编辑后记得点击保存按钮保存变更
74 |
75 | - [x] 添加命令
76 | - [x] 定义工作空间
77 | - [x] 定义变量空间
78 | - [x] 删除命令
79 | - [x] 运行命令
80 | - [x] 保存命令
81 |
82 | 
83 | 
84 |
85 | #### 日志监控
86 |
87 | 一个用户监控日志文件修改的应用,可以最高同时监控三个文件
88 | 因为过多的日志记录会使应用较为卡顿因此设定最大保留日志行数(默认为1000行)
89 |
90 | 
91 |
92 | #### 代理抓包
93 |
94 | 详见 [AnyProxy](https://github.com/alibaba/anyproxy)
95 | 默认端口:
96 | > 代理服务:`8001`
97 | > 网页服务:`8002`
98 |
99 | #### 加密解密
100 |
101 | 加密/解密字符串的各种算法格式,目前支持的算法有如下几种常见的算法:
102 | - `Unicode`
103 | - `UTF-8`
104 | - `URI`
105 | - `Base64`
106 | - `MD5`
107 | - `SHA1`
108 | - `AES`(需要提供秘钥)
109 |
110 | 
111 |
112 | #### 文本比较
113 |
114 | 快速比较两段文字的差异,高亮形式表现出差异段落
115 |
116 | 
117 |
118 | #### 二维码生成
119 |
120 | 快速生成二维码
121 | 常用于生成网页到手机端
122 |
123 | 
124 |
125 | #### JSON校验
126 | 用于JSON校验, 可以格式化不正确的JSON格式的字符串, 并指出不正确的地方
127 | 也可以用于美化JSON字符串
128 |
129 | 
130 |
131 | #### 系统进程
132 | 列出系统进程的:
133 | - 进程名
134 | - pid
135 | - 命令
136 | - cpu占用
137 |
138 | 并支持过滤匹配项, 与列表排序
139 |
140 | 
141 |
142 | #### 端口扫描
143 | 扫描目标ip(域名)监听的tcp端口
144 | 用法:
145 | - `127.0.0.1` 扫描本地所有的端口情况(1~65535)
146 | - `127.0.0.1:22` 扫描本地22端口情况
147 |
148 | 
149 |
150 | #### HTTP服务
151 | 开放一个简易的静态http服务, 用于局域网快速共享前端网页。但因为没有解释器所以不能运行动态脚本
152 |
153 | 
154 |
155 | #### FTP服务
156 | 开放一个简易的ftp服务,用于局域网快速共享文件
157 | 如果无法正常列出目录, 请尝试手动指定为主动模式
158 |
159 | 
160 |
161 | ---
162 |
163 | This project was generated with [electron-vue](https://github.com/SimulatedGREG/electron-vue) using [vue-cli](https://github.com/vuejs/vue-cli). Documentation about the original structure can be found [here](https://simulatedgreg.gitbooks.io/electron-vue/content/index.html).
164 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # Commented sections below can be used to run tests on the CI server
2 | # https://simulatedgreg.gitbooks.io/electron-vue/content/en/testing.html#on-the-subject-of-ci-testing
3 | version: 0.1.{build}
4 |
5 | branches:
6 | only:
7 | - master
8 |
9 | image: Visual Studio 2017
10 | platform:
11 | - x64
12 |
13 | cache:
14 | - node_modules
15 | - '%APPDATA%\npm-cache'
16 | - '%USERPROFILE%\.electron'
17 | - '%USERPROFILE%\AppData\Local\Yarn\cache'
18 |
19 | init:
20 | - git config --global core.autocrlf input
21 |
22 | install:
23 | - ps: Install-Product node 8 x64
24 | - choco install yarn --ignore-dependencies
25 | - git reset --hard HEAD
26 | - yarn
27 | - node --version
28 |
29 | build_script:
30 | #- yarn test
31 | - yarn build
32 |
33 | test: off
34 |
35 | artifacts:
36 | - path: build/*
37 |
--------------------------------------------------------------------------------
/build/icons/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/build/icons/256x256.png
--------------------------------------------------------------------------------
/build/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/build/icons/icon.icns
--------------------------------------------------------------------------------
/build/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/build/icons/icon.ico
--------------------------------------------------------------------------------
/dist/electron/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/dist/electron/.gitkeep
--------------------------------------------------------------------------------
/dist/web/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/dist/web/.gitkeep
--------------------------------------------------------------------------------
/doc/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/1.png
--------------------------------------------------------------------------------
/doc/10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/10.png
--------------------------------------------------------------------------------
/doc/11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/11.png
--------------------------------------------------------------------------------
/doc/12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/12.png
--------------------------------------------------------------------------------
/doc/13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/13.png
--------------------------------------------------------------------------------
/doc/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/2.png
--------------------------------------------------------------------------------
/doc/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/3.png
--------------------------------------------------------------------------------
/doc/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/4.png
--------------------------------------------------------------------------------
/doc/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/5.png
--------------------------------------------------------------------------------
/doc/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/6.png
--------------------------------------------------------------------------------
/doc/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/7.png
--------------------------------------------------------------------------------
/doc/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/8.png
--------------------------------------------------------------------------------
/doc/9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/9.png
--------------------------------------------------------------------------------
/doc/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/doc/menu.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "devbox",
3 | "version": "1.1.1",
4 | "author": "moonrailgun",
5 | "description": "An electron-vue project",
6 | "license": null,
7 | "main": "./dist/electron/main.js",
8 | "scripts": {
9 | "build": "node .electron-vue/build.js && electron-builder",
10 | "build:dir": "node .electron-vue/build.js && electron-builder --dir",
11 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js",
12 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js",
13 | "dev": "node .electron-vue/dev-runner.js",
14 | "e2e": "npm run pack && mocha test/e2e",
15 | "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test",
16 | "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test",
17 | "pack": "npm run pack:main && npm run pack:renderer",
18 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js",
19 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js",
20 | "test": "npm run unit && npm run e2e",
21 | "unit": "karma start test/unit/karma.conf.js",
22 | "postinstall": "npm run lint:fix"
23 | },
24 | "build": {
25 | "productName": "devbox",
26 | "appId": "com.moonrailgun.devbox",
27 | "directories": {
28 | "output": "build"
29 | },
30 | "files": [
31 | "dist/electron/**/*"
32 | ],
33 | "dmg": {
34 | "contents": [
35 | {
36 | "x": 410,
37 | "y": 150,
38 | "type": "link",
39 | "path": "/Applications"
40 | },
41 | {
42 | "x": 130,
43 | "y": 150,
44 | "type": "file"
45 | }
46 | ]
47 | },
48 | "mac": {
49 | "icon": "build/icons/icon.icns"
50 | },
51 | "win": {
52 | "icon": "build/icons/icon.ico"
53 | },
54 | "linux": {
55 | "icon": "build/icons"
56 | }
57 | },
58 | "dependencies": {
59 | "anyproxy": "^4.0.6",
60 | "axios": "^0.16.1",
61 | "crypto-js": "^3.1.9-1",
62 | "electron-log": "^2.2.14",
63 | "element-ui": "^2.3.3",
64 | "execa": "^0.10.0",
65 | "fs-extra": "^5.0.0",
66 | "ftp-srv": "^2.16.1",
67 | "html2canvas": "^1.0.0-alpha.10",
68 | "http-server": "^0.11.1",
69 | "lodash.debounce": "^4.0.8",
70 | "nedb": "^1.8.0",
71 | "port-numbers": "^2.0.26",
72 | "ps-list": "^4.0.0",
73 | "qrcode": "^1.2.0",
74 | "string-to-color": "^2.0.0",
75 | "tail": "^1.2.3",
76 | "vue": "^2.3.3",
77 | "vue-codemirror": "^4.0.3",
78 | "vue-electron": "^1.0.6",
79 | "vue-router": "^2.5.3",
80 | "vuex": "^2.3.1",
81 | "vuex-persist": "^1.1.1"
82 | },
83 | "devDependencies": {
84 | "babel-core": "^6.25.0",
85 | "babel-loader": "^7.1.1",
86 | "babel-plugin-transform-runtime": "^6.23.0",
87 | "babel-preset-env": "^1.6.0",
88 | "babel-preset-stage-0": "^6.24.1",
89 | "babel-register": "^6.24.1",
90 | "babili-webpack-plugin": "^0.1.2",
91 | "cfonts": "^1.1.3",
92 | "chalk": "^2.1.0",
93 | "copy-webpack-plugin": "^4.0.1",
94 | "cross-env": "^5.0.5",
95 | "css-loader": "^0.28.4",
96 | "del": "^3.0.0",
97 | "devtron": "^1.4.0",
98 | "electron": "^1.7.5",
99 | "electron-debug": "^1.4.0",
100 | "electron-devtools-installer": "^2.2.0",
101 | "electron-builder": "^19.19.1",
102 | "babel-eslint": "^7.2.3",
103 | "eslint": "^4.4.1",
104 | "eslint-friendly-formatter": "^3.0.0",
105 | "eslint-loader": "^1.9.0",
106 | "eslint-plugin-html": "^3.1.1",
107 | "eslint-config-standard": "^10.2.1",
108 | "eslint-plugin-import": "^2.7.0",
109 | "eslint-plugin-node": "^5.1.1",
110 | "eslint-plugin-promise": "^3.5.0",
111 | "eslint-plugin-standard": "^3.0.1",
112 | "extract-text-webpack-plugin": "^3.0.0",
113 | "file-loader": "^0.11.2",
114 | "html-webpack-plugin": "^2.30.1",
115 | "inject-loader": "^3.0.0",
116 | "karma": "^1.3.0",
117 | "karma-chai": "^0.1.0",
118 | "karma-coverage": "^1.1.1",
119 | "karma-electron": "^5.1.1",
120 | "karma-mocha": "^1.2.0",
121 | "karma-sourcemap-loader": "^0.3.7",
122 | "karma-spec-reporter": "^0.0.31",
123 | "karma-webpack": "^2.0.1",
124 | "webpack-merge": "^4.1.0",
125 | "require-dir": "^0.3.0",
126 | "spectron": "^3.7.1",
127 | "babel-plugin-istanbul": "^4.1.1",
128 | "chai": "^4.0.0",
129 | "mocha": "^3.0.2",
130 | "multispinner": "^0.2.1",
131 | "node-loader": "^0.6.0",
132 | "style-loader": "^0.18.2",
133 | "url-loader": "^0.5.9",
134 | "vue-html-loader": "^1.2.4",
135 | "vue-loader": "^13.0.5",
136 | "vue-style-loader": "^3.0.1",
137 | "vue-template-compiler": "^2.4.2",
138 | "webpack": "^3.5.2",
139 | "webpack-dev-server": "^2.7.1",
140 | "webpack-hot-middleware": "^2.18.2"
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | devbox
6 | <% if (htmlWebpackPlugin.options.nodeModules) { %>
7 |
8 |
11 | <% } %>
12 |
13 |
14 |
15 |
16 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/event/portscan.js:
--------------------------------------------------------------------------------
1 | import { ipcMain } from 'electron'
2 | import net from 'net'
3 |
4 | function scan (host, start, end, callback) {
5 | var count = end - start
6 | var result = []
7 | console.time('port scan time')
8 |
9 | for (var i = start; i <= end; i++) {
10 | var item = net.connect({
11 | host: host,
12 | port: i,
13 | timeout: 30000 // 增加半分钟的超时时间
14 | }, (function (i) {
15 | return function () {
16 | result.push(i)
17 | this.destroy()
18 | }
19 | }(i)))
20 |
21 | item.on('error', function (err) {
22 | if (err.errno === 'ECONNREFUSED') {
23 | this.destroy()
24 | }
25 | })
26 |
27 | item.on('close', function () {
28 | if (!count--) {
29 | console.timeEnd('port scan time')
30 | callback(result)
31 | }
32 | })
33 | }
34 | }
35 |
36 | ipcMain.on('port-scan', function (event, host, start = 1, end = 65535) {
37 | console.log('开始扫描: ', host, ' | 端口: ', start, ' ~ ', end);
38 |
39 | (function (host, start, end) {
40 | scan(host, start, end, function (result) {
41 | event.sender.send('port-scan-finished', host, result)
42 | })
43 | })(host, start, end)
44 | })
45 |
--------------------------------------------------------------------------------
/src/main/ftp/index.js:
--------------------------------------------------------------------------------
1 | const { ipcMain, app } = require('electron')
2 | const FtpSrv = require('ftp-srv')
3 | const log = require('electron-log')
4 |
5 | let ftpServer = null
6 | const homePath = app.getPath('home')
7 |
8 | ipcMain.on('ftp-server-start', function (event, options = undefined, error = null) {
9 | if (!ftpServer) {
10 | let port = options.port || 8880
11 | ftpServer = new FtpSrv(`ftp://0.0.0.0:${port}`, {
12 | pasv_range: 8881,
13 | greeting: ['hello', 'world'],
14 | anonymous: true,
15 | file_format: 'ep'
16 | })
17 | ftpServer.on('login', (data, resolve, reject) => {
18 | resolve({
19 | root: homePath
20 | })
21 | })
22 | ftpServer.listen().then(() => {
23 | log.info('ftp server start')
24 | })
25 | } else {
26 | log.info('ftp server isrunning!')
27 | }
28 | })
29 |
30 | ipcMain.on('ftp-server-stop', function (event) {
31 | if (ftpServer) {
32 | ftpServer.close().then(() => {
33 | log.info('ftp server stop')
34 | })
35 | }
36 | ftpServer = null
37 | })
38 |
--------------------------------------------------------------------------------
/src/main/http/index.js:
--------------------------------------------------------------------------------
1 | const { ipcMain, app } = require('electron')
2 | const { createServer } = require('http-server')
3 | const log = require('electron-log')
4 |
5 | let httpServer = null
6 | const homePath = app.getPath('home')
7 |
8 | const defaultOptions = {
9 | root: homePath,
10 | headers: {},
11 | cache: 3600,
12 | showDir: 'true',
13 | autoIndex: 'true',
14 | showDotfiles: false,
15 | gzip: true,
16 | contentType: 'application/octet-stream',
17 | ext: undefined,
18 | cors: undefined,
19 | proxy: undefined
20 | }
21 |
22 | ipcMain.on('http-server-start', function (event, port, options = undefined) {
23 | if (!httpServer) {
24 | httpServer = createServer(Object.assign({}, defaultOptions, options))
25 | httpServer.listen(port)
26 | log.info('http server start')
27 | } else {
28 | log.info('ftp server isrunning!')
29 | }
30 | })
31 |
32 | ipcMain.on('http-server-stop', function (event) {
33 | if (httpServer) {
34 | httpServer.close()
35 | log.info('http server stop')
36 | }
37 | httpServer = null
38 | })
39 |
--------------------------------------------------------------------------------
/src/main/index.dev.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is used specifically and only for development. It installs
3 | * `electron-debug` & `vue-devtools`. There shouldn't be any need to
4 | * modify this file, but it can be used to extend your development
5 | * environment.
6 | */
7 |
8 | /* eslint-disable */
9 |
10 | // Set environment for development
11 | process.env.NODE_ENV = 'development'
12 |
13 | // Install `electron-debug` with `devtron`
14 | require('electron-debug')({ showDevTools: true })
15 |
16 | // Install `vue-devtools`
17 | require('electron').app.on('ready', () => {
18 | let installExtension = require('electron-devtools-installer')
19 | installExtension.default(installExtension.VUEJS_DEVTOOLS)
20 | .then(() => {})
21 | .catch(err => {
22 | console.log('Unable to install `vue-devtools`: \n', err)
23 | })
24 | })
25 |
26 | // Require `main` process to boot app
27 | require('./index')
28 |
--------------------------------------------------------------------------------
/src/main/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import { app, BrowserWindow, Menu } from 'electron'
4 | import log from 'electron-log'
5 |
6 | /**
7 | * Set `__static` path to static files in production
8 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
9 | */
10 | if (process.env.NODE_ENV !== 'development') {
11 | global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
12 | }
13 |
14 | let mainWindow
15 | const winURL = process.env.NODE_ENV === 'development'
16 | ? `http://localhost:9080`
17 | : `file://${__dirname}/index.html`
18 |
19 | function createWindow () {
20 | /**
21 | * Initial window options
22 | */
23 | mainWindow = new BrowserWindow({
24 | height: 721,
25 | useContentSize: true,
26 | width: 1100,
27 | minWidth: 1100,
28 | minHeight: 721,
29 | webPreferences: {webSecurity: false}
30 | })
31 |
32 | // Menu
33 | const template = [{
34 | label: 'Edit',
35 | submenu: [
36 | {role: 'undo'},
37 | {role: 'redo'},
38 | {type: 'separator'},
39 | {role: 'cut'},
40 | {role: 'copy'},
41 | {role: 'paste'},
42 | {role: 'pasteandmatchstyle'},
43 | {role: 'delete'},
44 | {role: 'selectall'}
45 | ]
46 | }, {
47 | label: 'View',
48 | submenu: [
49 | {role: 'reload'},
50 | {role: 'forcereload'},
51 | {role: 'toggledevtools'},
52 | {type: 'separator'},
53 | {role: 'resetzoom'},
54 | {role: 'zoomin'},
55 | {role: 'zoomout'},
56 | {type: 'separator'},
57 | {role: 'togglefullscreen'}
58 | ]
59 | }, {
60 | role: 'window',
61 | submenu: [
62 | {role: 'minimize'},
63 | {role: 'close'}
64 | ]
65 | }, {
66 | role: 'help',
67 | submenu: [
68 | {
69 | label: 'Learn More',
70 | click () { require('electron').shell.openExternal('https://electronjs.org') }
71 | }
72 | ]
73 | }
74 | ]
75 |
76 | if (process.platform === 'darwin') {
77 | template.unshift({
78 | label: app.getName(),
79 | submenu: [
80 | {role: 'about'},
81 | {type: 'separator'},
82 | {role: 'services', submenu: []},
83 | {type: 'separator'},
84 | {role: 'hide'},
85 | {role: 'hideothers'},
86 | {role: 'unhide'},
87 | {type: 'separator'},
88 | {role: 'quit'}
89 | ]
90 | })
91 |
92 | // Edit menu
93 | template[1].submenu.push(
94 | {type: 'separator'},
95 | {
96 | label: 'Speech',
97 | submenu: [
98 | {role: 'startspeaking'},
99 | {role: 'stopspeaking'}
100 | ]
101 | }
102 | )
103 |
104 | // Window menu
105 | template[3].submenu = [
106 | {role: 'close'},
107 | {role: 'minimize'},
108 | {role: 'zoom'},
109 | {type: 'separator'},
110 | {role: 'front'}
111 | ]
112 | }
113 | const menu = Menu.buildFromTemplate(template)
114 | Menu.setApplicationMenu(menu)
115 |
116 | mainWindow.loadURL(winURL)
117 |
118 | mainWindow.on('resize', () => {
119 | mainWindow.send('resize')
120 | })
121 |
122 | mainWindow.on('closed', () => {
123 | mainWindow = null
124 | })
125 | }
126 |
127 | app.on('ready', createWindow)
128 |
129 | app.on('window-all-closed', () => {
130 | if (process.platform !== 'darwin') {
131 | app.quit()
132 | }
133 | })
134 |
135 | app.on('activate', () => {
136 | if (mainWindow === null) {
137 | createWindow()
138 | }
139 | })
140 |
141 | require('./event/portscan')
142 | require('./proxy/')
143 | require('./http/')
144 | require('./ftp/')
145 |
146 | log.info('app start completed!')
147 |
148 | /**
149 | * Auto Updater
150 | *
151 | * Uncomment the following code below and install `electron-updater` to
152 | * support auto updating. Code Signing with a valid certificate is required.
153 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-builder.html#auto-updating
154 | */
155 |
156 | /*
157 | import { autoUpdater } from 'electron-updater'
158 |
159 | autoUpdater.on('update-downloaded', () => {
160 | autoUpdater.quitAndInstall()
161 | })
162 |
163 | app.on('ready', () => {
164 | if (process.env.NODE_ENV === 'production') autoUpdater.checkForUpdates()
165 | })
166 | */
167 |
--------------------------------------------------------------------------------
/src/main/proxy/index.js:
--------------------------------------------------------------------------------
1 | const { ipcMain } = require('electron')
2 | const AnyProxy = require('anyproxy')
3 | const log = require('electron-log')
4 | const defaultOptions = {
5 | port: 8001,
6 | // rule: require('myRuleModule'),
7 | webInterface: {
8 | enable: true,
9 | webPort: 8002
10 | },
11 | throttle: 10000,
12 | forceProxyHttps: false,
13 | wsIntercept: false, // 不开启websocket代理
14 | silent: false
15 | }
16 |
17 | let isProxyStarted = false
18 | let proxyServer = null
19 |
20 | ipcMain.on('proxy-server-start', function (event, options = null) {
21 | if (!isProxyStarted) {
22 | proxyServer = new AnyProxy.ProxyServer(Object.assign({}, defaultOptions, options))
23 | proxyServer.on('ready', () => {
24 | log.info('[AnyProxy]:', 'ready')
25 | })
26 | proxyServer.on('error', (e) => {
27 | log.error('[AnyProxy]:', e)
28 | })
29 | proxyServer.start()
30 | isProxyStarted = true
31 | } else {
32 | log.info('[AnyProxy]:', 'proxy server has been started!')
33 | }
34 | })
35 |
36 | ipcMain.on('proxy-server-stop', function (event) {
37 | if (isProxyStarted) {
38 | proxyServer.close()
39 | isProxyStarted = false
40 | } else {
41 | log.info('[AnyProxy]:', 'proxy server has been closed!')
42 | }
43 | })
44 |
--------------------------------------------------------------------------------
/src/renderer/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
16 |
--------------------------------------------------------------------------------
/src/renderer/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/src/renderer/assets/.gitkeep
--------------------------------------------------------------------------------
/src/renderer/assets/global.css:
--------------------------------------------------------------------------------
1 | code {
2 | padding: 0.2em 0.4em;
3 | margin: 0;
4 | font-size: 85%;
5 | background-color: rgba(27,31,35,0.05);
6 | border-radius: 3px;
7 | vertical-align: middle;
8 | }
9 |
--------------------------------------------------------------------------------
/src/renderer/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/src/renderer/assets/logo.png
--------------------------------------------------------------------------------
/src/renderer/components/Deprecated/ColorCard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 添加到我的色卡
7 | {{pickerColor}}
8 |
11 |
12 |
13 |
14 |
15 |
16 | 我的色卡
17 |
20 |
21 |
22 | 选择颜色
23 | 色值:{{selectedColor.val}}
24 | 添加时间:{{selectedColor.createdAt}}
25 | 备注:{{selectedColor.comments}}
26 | 移除
27 |
28 |
29 |
30 |
31 |
32 |
63 |
64 |
86 |
--------------------------------------------------------------------------------
/src/renderer/components/Deprecated/Doc.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | {{ node.label }}
11 |
12 | append(data)">
16 | 添加
17 |
18 | remove(node, data)">
23 | 移除
24 |
25 |
26 |
27 |
28 |
29 |
30 | 2
31 |
32 |
33 |
34 |
35 |
49 |
50 |
56 |
--------------------------------------------------------------------------------
/src/renderer/components/Deprecated/Http.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 | :
15 |
16 |
17 |
18 |
19 | :
20 |
21 |
22 |
23 |
24 |
30 |
31 |
32 |
33 |
34 |
35 |
65 |
66 |
99 |
--------------------------------------------------------------------------------
/src/renderer/components/Deprecated/SSHEmulator.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
70 |
71 |
92 |
--------------------------------------------------------------------------------
/src/renderer/components/LandingPage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
59 |
60 |
152 |
--------------------------------------------------------------------------------
/src/renderer/components/LandingPage/SystemInformation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Information
4 |
5 |
6 |
Path:
7 |
{{ path }}
8 |
9 |
10 |
Route Name:
11 |
{{ name }}
12 |
13 |
14 |
Vue.js:
15 |
{{ vue }}
16 |
17 |
18 |
Electron:
19 |
{{ electron }}
20 |
21 |
22 |
Node:
23 |
{{ node }}
24 |
25 |
26 |
Platform:
27 |
{{ platform }}
28 |
29 |
30 |
31 |
32 |
33 |
47 |
48 |
74 |
--------------------------------------------------------------------------------
/src/renderer/components/LeftMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
45 |
46 |
47 |
70 |
71 |
83 |
--------------------------------------------------------------------------------
/src/renderer/components/MainView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
61 |
62 |
69 |
--------------------------------------------------------------------------------
/src/renderer/components/View/AnyProxy.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 启动服务
4 | 停止服务
5 | 打开web界面
6 |
7 |
8 |
9 |
59 |
--------------------------------------------------------------------------------
/src/renderer/components/View/CodeSnippet.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 |
16 |
21 |
22 |
23 |
24 |
25 |
26 | 保 存
27 |
28 |
29 |
30 |
31 |
32 |
33 | {{snippet.language}}
34 |
35 |
36 |
{{snippet.name}}
37 |
38 |
39 | 移除
40 | 编辑
41 | 复制
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 添加新的片段
51 |
52 |
53 |
54 |
55 |
56 |
57 |
188 |
189 |
245 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Exec.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | 可以使用${variable}形式插入变量
21 |
22 |
23 |
24 |
25 | 配置变量默认值, 可为空
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
38 | {{shellLog}}
39 |
40 |
41 |
42 | 添加命令
43 | 保存
44 |
45 |
46 |
47 |
48 |
49 | 运行
50 | 展开
56 | command()">
57 |
58 |
59 |
60 |
61 | 删除
62 |
63 |
64 |
65 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
156 |
157 |
216 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Settings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 代理服务
12 |
13 |
14 | 网页服务
15 |
16 |
17 |
18 | 导出配置
19 | 导入配置
20 |
21 |
22 |
23 |
24 |
98 |
99 |
105 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Str/StrBejson.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
校验
5 |
清空
6 |
7 |
{{resultText}}
8 |
9 |
10 |
11 |
12 |
59 |
60 |
83 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Str/StrDiff.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 进行比较
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
61 |
62 |
102 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Str/StrEncode.vue:
--------------------------------------------------------------------------------
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 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
200 |
201 |
214 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Str/StrQrcode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
生成二维码
4 |
5 |
生成二维码
6 |
清除
7 |
二维码内容: {{qrcodeStr}}
8 |
![]()
9 |
10 |
11 |
12 |
42 |
43 |
52 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Sys/FTPServer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
启动服务
4 |
停止服务
5 |
6 | 端口号
7 |
8 |
9 |
10 | 地址: ftp://0.0.0.0:8880
11 |
12 |
13 | 如果出现无法执行LIST
命令的情况,请尝试手动切换到主动模式
14 |
15 |
16 |
17 |
18 |
19 |
56 |
57 |
67 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Sys/HTTPServer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
启动服务
4 |
停止服务
5 |
6 |
7 |
8 |
9 |
10 |
11 | 选择文件夹
12 | 当前根目录:{{info.rootDir}}
13 |
14 |
15 |
16 |
17 |
18 |
78 |
79 |
86 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Sys/SysPortScan.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
端口扫描
4 |
5 |
输入address格式扫描该地址全端口,输入address:port扫描该地址固定端口
6 |
{{!isScanning?"开始扫描":"扫描中"}}
7 |
扫描主机: {{scanResult.host}} 报告
8 |
9 |
13 |
14 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
90 |
91 |
100 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Sys/SysProcess.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{!isScanning?"开始扫描":"扫描中"}}
5 |
6 |
11 |
12 |
16 |
17 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
66 |
67 |
76 |
--------------------------------------------------------------------------------
/src/renderer/components/View/Tail.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | |
8 | {{log.message}} |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
日志文件监控
23 |
35 | 选择日志文件
36 | 请选择能使用文本形式能够正常打开的文件
37 |
38 |
39 | 开始监听
40 |
41 |
42 | 停止监听
43 |
44 |
45 | 清空
46 | 行数:{{lines}}
47 |
48 |
49 |
50 |
51 |
52 |
53 |
154 |
155 |
226 |
--------------------------------------------------------------------------------
/src/renderer/datastore.js:
--------------------------------------------------------------------------------
1 | import Datastore from 'nedb'
2 | import path from 'path'
3 | import { remote } from 'electron'
4 |
5 | console.log('用户文件夹', remote.app.getPath('userData'))
6 |
7 | export default new Datastore({
8 | autoload: true,
9 | filename: path.join(remote.app.getPath('userData'), '/data.db')
10 | })
11 |
--------------------------------------------------------------------------------
/src/renderer/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import axios from 'axios'
3 | import ElementUI from 'element-ui'
4 | import 'element-ui/lib/theme-chalk/index.css'
5 |
6 | import VueCodemirror from 'vue-codemirror'
7 | import 'codemirror/lib/codemirror.css'
8 | import 'codemirror/mode/javascript/javascript.js'
9 |
10 | import App from './App'
11 | import router from './router'
12 | import store from './store'
13 |
14 | import db from './datastore'
15 |
16 | import './utils/common'
17 | import './assets/global.css'
18 |
19 | if (!process.env.IS_WEB) Vue.use(require('vue-electron'))
20 | Vue.http = Vue.prototype.$http = axios
21 | Vue.config.productionTip = false
22 | Vue.prototype.$db = db
23 |
24 | Vue.use(ElementUI)
25 | Vue.use(VueCodemirror)
26 |
27 | /* eslint-disable no-new */
28 | new Vue({
29 | components: { App },
30 | router,
31 | store,
32 | template: ''
33 | }).$mount('#app')
34 |
--------------------------------------------------------------------------------
/src/renderer/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Router from 'vue-router'
3 |
4 | Vue.use(Router)
5 |
6 | export default new Router({
7 | routes: [
8 | {
9 | path: '/',
10 | name: 'landing-page',
11 | component: require('@/components/LandingPage').default
12 | },
13 | {
14 | path: '*',
15 | redirect: '/'
16 | }
17 | ]
18 | })
19 |
--------------------------------------------------------------------------------
/src/renderer/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import VuexPersistence from 'vuex-persist'
4 |
5 | import modules from './modules'
6 |
7 | Vue.use(Vuex)
8 |
9 | const vuexLocal = new VuexPersistence({
10 | storage: window.localStorage,
11 | modules: ['Settings', 'Exec']
12 | })
13 |
14 | export default new Vuex.Store({
15 | modules,
16 | plugins: [vuexLocal.plugin],
17 | strict: process.env.NODE_ENV !== 'production'
18 | })
19 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/Counter.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | main: 0
3 | }
4 |
5 | const mutations = {
6 | DECREMENT_MAIN_COUNTER (state) {
7 | state.main--
8 | },
9 | INCREMENT_MAIN_COUNTER (state) {
10 | state.main++
11 | }
12 | }
13 |
14 | const actions = {
15 | someAsyncTask ({ commit }) {
16 | // do something async
17 | commit('INCREMENT_MAIN_COUNTER')
18 | }
19 | }
20 |
21 | export default {
22 | state,
23 | mutations,
24 | actions
25 | }
26 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/Exec.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | scripts: []
3 | }
4 |
5 | const mutations = {
6 | MODIFY_EXEC_SCRIPTS (state, {data}) {
7 | state.scripts = data
8 | }
9 | }
10 |
11 | const actions = {
12 | modifyExecScripts ({ commit }, payload) {
13 | commit('MODIFY_EXEC_SCRIPTS', {data: payload})
14 | }
15 | }
16 |
17 | export default {
18 | state,
19 | mutations,
20 | actions
21 | }
22 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/Nav.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | curNav: 'snippet'
3 | }
4 |
5 | const mutations = {
6 | SET_NAV (state, {navName}) {
7 | state.curNav = navName
8 | }
9 | }
10 |
11 | const actions = {
12 | setNav ({ commit }, name) {
13 | commit('SET_NAV', {navName: name})
14 | }
15 | }
16 |
17 | export default {
18 | state,
19 | mutations,
20 | actions
21 | }
22 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/Settings.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | settings: {
3 | tailMaxLine: 1000,
4 | menuCollapse: false
5 | }
6 | }
7 |
8 | const mutations = {
9 | MODIFY_SETTINGS (state, {data}) {
10 | state.settings = Object.assign({}, state.settings, data)
11 | }
12 | }
13 |
14 | const actions = {
15 | modifySettings ({ commit }, payload) {
16 | commit('MODIFY_SETTINGS', {data: payload})
17 | }
18 | }
19 |
20 | const getters = {
21 | asideWidth: state => state.settings.menuCollapse ? 64 : 201
22 | }
23 |
24 | export default {
25 | state,
26 | getters,
27 | mutations,
28 | actions
29 | }
30 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/View.js:
--------------------------------------------------------------------------------
1 | const state = {
2 | docTree: [{
3 | id: 0,
4 | label: '文档'
5 | }]
6 | }
7 |
8 | const mutations = {
9 | MODIFY_DOC_TREE (state, {data}) {
10 | state.docTree = Object.assign({}, state.docTree, data)
11 | }
12 | }
13 |
14 | const actions = {
15 | modifyDocTree ({ commit }, payload) {
16 | commit('MODIFY_DOC_TREE', {data: payload})
17 | }
18 | }
19 |
20 | export default {
21 | state,
22 | mutations,
23 | actions
24 | }
25 |
--------------------------------------------------------------------------------
/src/renderer/store/modules/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * The file enables `@/store/index.js` to import all vuex modules
3 | * in a one-shot manner. There should not be any reason to edit this file.
4 | */
5 |
6 | const files = require.context('.', false, /\.js$/)
7 | const modules = {}
8 |
9 | files.keys().forEach(key => {
10 | if (key === './index.js') return
11 | modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
12 | })
13 |
14 | export default modules
15 |
--------------------------------------------------------------------------------
/src/renderer/utils/bejson.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | let Bejson = {};
3 |
4 | Bejson.parser = function() {
5 | var a = !0,
6 | b = !1,
7 | c = {},
8 | d = function() {
9 | var a = {
10 | trace: function() {},
11 | yy: {},
12 | symbols_: {
13 | error: 2,
14 | JSONString: 3,
15 | STRING: 4,
16 | JSONNumber: 5,
17 | NUMBER: 6,
18 | JSONNullLiteral: 7,
19 | NULL: 8,
20 | JSONBooleanLiteral: 9,
21 | TRUE: 10,
22 | FALSE: 11,
23 | JSONText: 12,
24 | JSONObject: 13,
25 | EOF: 14,
26 | JSONArray: 15,
27 | JSONValue: 16,
28 | "{": 17,
29 | "}": 18,
30 | JSONMemberList: 19,
31 | JSONMember: 20,
32 | ":": 21,
33 | ",": 22,
34 | "[": 23,
35 | "]": 24,
36 | JSONElementList: 25,
37 | $accept: 0,
38 | $end: 1
39 | },
40 | terminals_: {
41 | 2: "error",
42 | 4: "STRING",
43 | 6: "NUMBER",
44 | 8: "NULL",
45 | 10: "TRUE",
46 | 11: "FALSE",
47 | 14: "EOF",
48 | 17: "{",
49 | 18: "}",
50 | 21: ":",
51 | 22: ",",
52 | 23: "[",
53 | 24: "]"
54 | },
55 | productions_: [0, [3, 1],
56 | [5, 1],
57 | [7, 1],
58 | [9, 1],
59 | [9, 1],
60 | [12, 2],
61 | [12, 2],
62 | [16, 1],
63 | [16, 1],
64 | [16, 1],
65 | [16, 1],
66 | [16, 1],
67 | [16, 1],
68 | [13, 2],
69 | [13, 3],
70 | [20, 3],
71 | [19, 1],
72 | [19, 3],
73 | [15, 2],
74 | [15, 3],
75 | [25, 1],
76 | [25, 3]
77 | ],
78 | performAction: function(a, b, c, d, e, f, g) {
79 | var h = f.length - 1;
80 | switch (e) {
81 | case 1:
82 | this.$ = a;
83 | break;
84 | case 2:
85 | this.$ = Number(a);
86 | break;
87 | case 3:
88 | this.$ = null;
89 | break;
90 | case 4:
91 | this.$ = !0;
92 | break;
93 | case 5:
94 | this.$ = !1;
95 | break;
96 | case 6:
97 | return this.$ = f[h - 1];
98 | case 7:
99 | return this.$ = f[h - 1];
100 | case 8:
101 | this.$ = f[h];
102 | break;
103 | case 9:
104 | this.$ = f[h];
105 | break;
106 | case 10:
107 | this.$ = f[h];
108 | break;
109 | case 11:
110 | this.$ = f[h];
111 | break;
112 | case 12:
113 | this.$ = f[h];
114 | break;
115 | case 13:
116 | this.$ = f[h];
117 | break;
118 | case 14:
119 | this.$ = {};
120 | break;
121 | case 15:
122 | this.$ = f[h - 1];
123 | break;
124 | case 16:
125 | this.$ = [f[h - 2], f[h]];
126 | break;
127 | case 17:
128 | this.$ = {}, this.$[f[h][0]] = f[h][1];
129 | break;
130 | case 18:
131 | this.$ = f[h - 2], f[h - 2][f[h][0]] = f[h][1];
132 | break;
133 | case 19:
134 | this.$ = [];
135 | break;
136 | case 20:
137 | this.$ = f[h - 1];
138 | break;
139 | case 21:
140 | this.$ = [f[h]];
141 | break;
142 | case 22:
143 | this.$ = f[h - 2], f[h - 2].push(f[h])
144 | }
145 | },
146 | table: [{
147 | 12: 1,
148 | 13: 2,
149 | 15: 3,
150 | 17: [1, 4],
151 | 23: [1, 5]
152 | }, {
153 | 1: [3]
154 | }, {
155 | 14: [1, 6]
156 | }, {
157 | 14: [1, 7]
158 | }, {
159 | 3: 11,
160 | 4: [1, 12],
161 | 18: [1, 8],
162 | 19: 9,
163 | 20: 10
164 | }, {
165 | 3: 18,
166 | 4: [1, 12],
167 | 5: 19,
168 | 6: [1, 25],
169 | 7: 16,
170 | 8: [1, 22],
171 | 9: 17,
172 | 10: [1, 23],
173 | 11: [1, 24],
174 | 13: 20,
175 | 15: 21,
176 | 16: 15,
177 | 17: [1, 4],
178 | 23: [1, 5],
179 | 24: [1, 13],
180 | 25: 14
181 | }, {
182 | 1: [2, 6]
183 | }, {
184 | 1: [2, 7]
185 | }, {
186 | 14: [2, 14],
187 | 18: [2, 14],
188 | 22: [2, 14],
189 | 24: [2, 14]
190 | }, {
191 | 18: [1, 26],
192 | 22: [1, 27]
193 | }, {
194 | 18: [2, 17],
195 | 22: [2, 17]
196 | }, {
197 | 21: [1, 28]
198 | }, {
199 | 18: [2, 1],
200 | 21: [2, 1],
201 | 22: [2, 1],
202 | 24: [2, 1]
203 | }, {
204 | 14: [2, 19],
205 | 18: [2, 19],
206 | 22: [2, 19],
207 | 24: [2, 19]
208 | }, {
209 | 22: [1, 30],
210 | 24: [1, 29]
211 | }, {
212 | 22: [2, 21],
213 | 24: [2, 21]
214 | }, {
215 | 18: [2, 8],
216 | 22: [2, 8],
217 | 24: [2, 8]
218 | }, {
219 | 18: [2, 9],
220 | 22: [2, 9],
221 | 24: [2, 9]
222 | }, {
223 | 18: [2, 10],
224 | 22: [2, 10],
225 | 24: [2, 10]
226 | }, {
227 | 18: [2, 11],
228 | 22: [2, 11],
229 | 24: [2, 11]
230 | }, {
231 | 18: [2, 12],
232 | 22: [2, 12],
233 | 24: [2, 12]
234 | }, {
235 | 18: [2, 13],
236 | 22: [2, 13],
237 | 24: [2, 13]
238 | }, {
239 | 18: [2, 3],
240 | 22: [2, 3],
241 | 24: [2, 3]
242 | }, {
243 | 18: [2, 4],
244 | 22: [2, 4],
245 | 24: [2, 4]
246 | }, {
247 | 18: [2, 5],
248 | 22: [2, 5],
249 | 24: [2, 5]
250 | }, {
251 | 18: [2, 2],
252 | 22: [2, 2],
253 | 24: [2, 2]
254 | }, {
255 | 14: [2, 15],
256 | 18: [2, 15],
257 | 22: [2, 15],
258 | 24: [2, 15]
259 | }, {
260 | 3: 11,
261 | 4: [1, 12],
262 | 20: 31
263 | }, {
264 | 3: 18,
265 | 4: [1, 12],
266 | 5: 19,
267 | 6: [1, 25],
268 | 7: 16,
269 | 8: [1, 22],
270 | 9: 17,
271 | 10: [1, 23],
272 | 11: [1, 24],
273 | 13: 20,
274 | 15: 21,
275 | 16: 32,
276 | 17: [1, 4],
277 | 23: [1, 5]
278 | }, {
279 | 14: [2, 20],
280 | 18: [2, 20],
281 | 22: [2, 20],
282 | 24: [2, 20]
283 | }, {
284 | 3: 18,
285 | 4: [1, 12],
286 | 5: 19,
287 | 6: [1, 25],
288 | 7: 16,
289 | 8: [1, 22],
290 | 9: 17,
291 | 10: [1, 23],
292 | 11: [1, 24],
293 | 13: 20,
294 | 15: 21,
295 | 16: 33,
296 | 17: [1, 4],
297 | 23: [1, 5]
298 | }, {
299 | 18: [2, 18],
300 | 22: [2, 18]
301 | }, {
302 | 18: [2, 16],
303 | 22: [2, 16]
304 | }, {
305 | 22: [2, 22],
306 | 24: [2, 22]
307 | }],
308 | defaultActions: {
309 | 6: [2, 6],
310 | 7: [2, 7]
311 | },
312 | parseError: function(a, b) {
313 | throw new Error(a)
314 | },
315 | parse: function(a) {
316 | function o() {
317 | var a;
318 | a = b.lexer.lex() || 1, typeof a != "number" && (a = b.symbols_[a] || a);
319 | return a
320 | }
321 |
322 | function n(a) {
323 | c.length = c.length - 2 * a, d.length = d.length - a, e.length = e.length - a
324 | }
325 | var b = this,
326 | c = [0],
327 | d = [null],
328 | e = [],
329 | f = this.table,
330 | g = "",
331 | h = 0,
332 | i = 0,
333 | j = 0,
334 | k = 2,
335 | l = 1;
336 | this.lexer.setInput(a), this.lexer.yy = this.yy, this.yy.lexer = this.lexer, typeof this.lexer.yylloc == "undefined" && (this.lexer.yylloc = {});
337 | var m = this.lexer.yylloc;
338 | e.push(m), typeof this.yy.parseError == "function" && (this.parseError = this.yy.parseError);
339 | var p, q, r, s, t, u, v = {},
340 | w, x, y, z;
341 | for (;;) {
342 | r = c[c.length - 1], this.defaultActions[r] ? s = this.defaultActions[r] : (p == null && (p = o()), s = f[r] && f[r][p]);
343 | if (typeof s == "undefined" || !s.length || !s[0]) {
344 | if (!j) {
345 | z = [];
346 | for (w in f[r]) this.terminals_[w] && w > 2 && z.push("'" + this.terminals_[w] + "'");
347 | var A = "";
348 | this.lexer.showPosition ? A = "Parse error on line " + (h + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + z.join(", ") : A = "Parse error on line " + (h + 1) + ": Unexpected " + (p == 1 ? "end of input" : "'" + (this.terminals_[p] || p) + "'"), this.parseError(A, {
349 | text: this.lexer.match,
350 | token: this.terminals_[p] || p,
351 | line: this.lexer.yylineno,
352 | loc: m,
353 | expected: z
354 | })
355 | }
356 | if (j == 3) {
357 | if (p == l) throw new Error(A || "Parsing halted.");
358 | i = this.lexer.yyleng, g = this.lexer.yytext, h = this.lexer.yylineno, m = this.lexer.yylloc, p = o()
359 | }
360 | for (;;) {
361 | if (k.toString() in f[r]) break;
362 | if (r == 0) throw new Error(A || "Parsing halted.");
363 | n(1), r = c[c.length - 1]
364 | }
365 | q = p, p = k, r = c[c.length - 1], s = f[r] && f[r][k], j = 3
366 | }
367 | if (s[0] instanceof Array && s.length > 1) throw new Error("Parse Error: multiple actions possible at state: " + r + ", token: " + p);
368 | switch (s[0]) {
369 | case 1:
370 | c.push(p), d.push(this.lexer.yytext), e.push(this.lexer.yylloc), c.push(s[1]), p = null, q ? (p = q, q = null) : (i = this.lexer.yyleng, g = this.lexer.yytext, h = this.lexer.yylineno, m = this.lexer.yylloc, j > 0 && j--);
371 | break;
372 | case 2:
373 | x = this.productions_[s[1]][1], v.$ = d[d.length - x], v._$ = {
374 | first_line: e[e.length - (x || 1)].first_line,
375 | last_line: e[e.length - 1].last_line,
376 | first_column: e[e.length - (x || 1)].first_column,
377 | last_column: e[e.length - 1].last_column
378 | }, u = this.performAction.call(v, g, i, h, this.yy, s[1], d, e);
379 | if (typeof u != "undefined") return u;
380 | x && (c = c.slice(0, -1 * x * 2), d = d.slice(0, -1 * x), e = e.slice(0, -1 * x)), c.push(this.productions_[s[1]][0]), d.push(v.$), e.push(v._$), y = f[c[c.length - 2]][c[c.length - 1]], c.push(y);
381 | break;
382 | case 3:
383 | return !0
384 | }
385 | }
386 | return !0
387 | }
388 | },
389 | f = function() {
390 | var a = {
391 | EOF: 1,
392 | parseError: function(a, b) {
393 | if (this.yy.parseError) this.yy.parseError(a, b);
394 | else throw new Error(a)
395 | },
396 | setInput: function(a) {
397 | this._input = a, this._more = this._less = this.done = !1, this.yylineno = this.yyleng = 0, this.yytext = this.matched = this.match = "", this.conditionStack = ["INITIAL"], this.yylloc = {
398 | first_line: 1,
399 | first_column: 0,
400 | last_line: 1,
401 | last_column: 0
402 | };
403 | return this
404 | },
405 | input: function() {
406 | var a = this._input[0];
407 | this.yytext += a, this.yyleng++, this.match += a, this.matched += a;
408 | var b = a.match(/\n/);
409 | b && this.yylineno++, this._input = this._input.slice(1);
410 | return a
411 | },
412 | unput: function(a) {
413 | this._input = a + this._input;
414 | return this
415 | },
416 | more: function() {
417 | this._more = !0;
418 | return this
419 | },
420 | pastInput: function() {
421 | var a = this.matched.substr(0, this.matched.length - this.match.length);
422 | return (a.length > 20 ? "..." : "") + a.substr(-20).replace(/\n/g, "")
423 | },
424 | upcomingInput: function() {
425 | var a = this.match;
426 | a.length < 20 && (a += this._input.substr(0, 20 - a.length));
427 | return (a.substr(0, 20) + (a.length > 20 ? "..." : "")).replace(/\n/g, "")
428 | },
429 | showPosition: function() {
430 | var a = this.pastInput(),
431 | b = Array(a.length + 1).join("-");
432 | return a + this.upcomingInput() + "\n" + b + "^"
433 | },
434 | next: function() {
435 | if (this.done) return this.EOF;
436 | this._input || (this.done = !0);
437 | var a, b, c, d;
438 | this._more || (this.yytext = "", this.match = "");
439 | var e = this._currentRules();
440 | for (var f = 0; f < e.length; f++) {
441 | b = this._input.match(this.rules[e[f]]);
442 | if (b) {
443 | d = b[0].match(/\n.*/g), d && (this.yylineno += d.length), this.yylloc = {
444 | first_line: this.yylloc.last_line,
445 | last_line: this.yylineno + 1,
446 | first_column: this.yylloc.last_column,
447 | last_column: d ? d[d.length - 1].length - 1 : this.yylloc.last_column + b[0].length
448 | }, this.yytext += b[0], this.match += b[0], this.matches = b, this.yyleng = this.yytext.length, this._more = !1, this._input = this._input.slice(b[0].length), this.matched += b[0], a = this.performAction.call(this, this.yy, this, e[f], this.conditionStack[this.conditionStack.length - 1]);
449 | if (a) return a;
450 | return
451 | }
452 | }
453 | if (this._input === "") return this.EOF;
454 | this.parseError("Lexical error on line " + (this.yylineno + 1) + ". Unrecognized text.\n" + this.showPosition(), {
455 | text: "",
456 | token: null,
457 | line: this.yylineno
458 | })
459 | },
460 | lex: function() {
461 | var a = this.next();
462 | return typeof a != "undefined" ? a : this.lex()
463 | },
464 | begin: function(a) {
465 | this.conditionStack.push(a)
466 | },
467 | popState: function() {
468 | return this.conditionStack.pop()
469 | },
470 | _currentRules: function() {
471 | return this.conditions[this.conditionStack[this.conditionStack.length - 1]].rules
472 | }
473 | };
474 | a.performAction = function(a, b, c, d) {
475 | var e = d;
476 | switch (c) {
477 | case 0:
478 | break;
479 | case 1:
480 | return 6;
481 | case 2:
482 | b.yytext = b.yytext.substr(1, b.yyleng - 2);
483 | return 4;
484 | case 3:
485 | return 17;
486 | case 4:
487 | return 18;
488 | case 5:
489 | return 23;
490 | case 6:
491 | return 24;
492 | case 7:
493 | return 22;
494 | case 8:
495 | return 21;
496 | case 9:
497 | return 10;
498 | case 10:
499 | return 11;
500 | case 11:
501 | return 8;
502 | case 12:
503 | return 14;
504 | case 13:
505 | return "INVALID"
506 | }
507 | }, a.rules = [
508 | /^\s+/, /^-?([0-9]|[1-9][0-9]+)(\.[0-9]+)?([eE][-+]?[0-9]+)?\b/,
509 | /^"(\\["bfnrt/\\]|\\u[a-fA-F0-9]{4}|[^\0-\x09\x0a-\x1f"\\])*"/,
510 | /^\{/,
511 | /^\}/,
512 | /^\[/,
513 | /^\]/,
514 | /^,/,
515 | /^:/,
516 | /^true\b/,
517 | /^false\b/,
518 | /^null\b/,
519 | /^$/,
520 | /^./
521 | ], a.conditions = {
522 | INITIAL: {
523 | rules: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
524 | inclusive: !0
525 | }
526 | };
527 | return a
528 | }();
529 | a.lexer = f;
530 | return a
531 | }();
532 | typeof a != "undefined" && typeof c != "undefined" && (c.parser = d, c.parse = function() {
533 | return d.parse.apply(d, arguments)
534 | }, c.main = function(b) {
535 | if (!b[1]) throw new Error("Usage: " + b[0] + " FILE");
536 | if (typeof process != "undefined") var d = a("fs").readFileSync(a("path").join(process.cwd(), b[1]), "utf8");
537 | else var e = a("file").path(a("file").cwd()),
538 | d = e.join(b[1]).read({
539 | charset: "utf-8"
540 | });
541 | return c.parser.parse(d)
542 | }, typeof b != "undefined" && a.main === b && c.main(typeof process != "undefined" ? process.argv.slice(1) : a("system").args));
543 | return c
544 | }();
545 | Bejson.format = (function() {
546 | function repeat(s, count) {
547 | return new Array(count + 1).join(s)
548 | }
549 |
550 | function formatJson(json) {
551 | var i = 0,
552 | il = 0,
553 | tab = " ",// change
554 | newJson = "",
555 | indentLevel = 0,
556 | inString = false,
557 | currentChar = null;
558 | for (i = 0, il = json.length; i < il; i += 1) {
559 | currentChar = json.charAt(i);
560 | switch (currentChar) {
561 | case '{':
562 | case '[':
563 | if (!inString) {
564 | newJson += currentChar + "\n" + repeat(tab, indentLevel + 1);
565 | indentLevel += 1
566 | } else {
567 | newJson += currentChar
568 | }
569 | break;
570 | case '}':
571 | case ']':
572 | if (!inString) {
573 | indentLevel -= 1;
574 | newJson += "\n" + repeat(tab, indentLevel) + currentChar
575 | } else {
576 | newJson += currentChar
577 | }
578 | break;
579 | case ',':
580 | if (!inString) {
581 | newJson += ",\n" + repeat(tab, indentLevel)
582 | } else {
583 | newJson += currentChar
584 | }
585 | break;
586 | case ':':
587 | if (!inString) {
588 | newJson += ": "
589 | } else {
590 | newJson += currentChar
591 | }
592 | break;
593 | case ' ':
594 | case "\n":
595 | case "\t":
596 | if (inString) {
597 | newJson += currentChar
598 | }
599 | break;
600 | case '"':
601 | if (i > 0 && json.charAt(i - 1) !== '\\') {
602 | inString = !inString
603 | }
604 | newJson += currentChar;
605 | break;
606 | default:
607 | newJson += currentChar;
608 | break
609 | }
610 | }
611 | return newJson
612 | }
613 | return {
614 | "formatJson": formatJson
615 | }
616 | }());
617 |
618 | module.exports = Bejson;
619 |
--------------------------------------------------------------------------------
/src/renderer/utils/common.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | Date.prototype.format = function (format) {
3 | var o = {
4 | 'M+': this.getMonth() + 1, // month
5 | 'd+': this.getDate(), // day
6 | 'h+': this.getHours(), // hour
7 | 'm+': this.getMinutes(), // minute
8 | 's+': this.getSeconds(), // second
9 | 'q+': Math.floor((this.getMonth() + 3) / 3), // quarter
10 | 'S': this.getMilliseconds() // millisecond
11 | }
12 | if (/(y+)/.test(format)) {
13 | format = format.replace(RegExp.$1,
14 | (this.getFullYear() + '').substr(4 - RegExp.$1.length))
15 | }
16 | for (var k in o) {
17 | if (new RegExp('(' + k + ')').test(format)) {
18 | format = format.replace(RegExp.$1,
19 | RegExp.$1.length === 1 ? o[k]
20 | : ('00' + o[k]).substr(('' + o[k]).length))
21 | }
22 | }
23 | return format
24 | }
25 |
--------------------------------------------------------------------------------
/src/renderer/utils/difflib.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | let __whitespace = {
3 | " ": true,
4 | "\t": true,
5 | "\n": true,
6 | "\f": true,
7 | "\r": true
8 | };
9 | let difflib = {
10 | defaultJunkFunction: function (c) {
11 | return c in __whitespace;
12 | },
13 | stripLinebreaks: function (str) {
14 | return str.replace(/^[\n\r]*|[\n\r]*$/g, "");
15 | },
16 | stringAsLines: function (str) {
17 | var lfpos = str.indexOf("\n");
18 | var crpos = str.indexOf("\r");
19 | var linebreak = ((lfpos > -1 && crpos > -1) || crpos < 0) ? "\n" : "\r";
20 | var lines = str.split(linebreak);
21 | for (var i = 0; i < lines.length; i++) {
22 | lines[i] = difflib.stripLinebreaks(lines[i]);
23 | }
24 | return lines;
25 | },
26 | __reduce: function (func, list, initial) {
27 | if (initial != null) {
28 | var value = initial;
29 | var idx = 0;
30 | } else if (list) {
31 | var value = list[0];
32 | var idx = 1;
33 | } else {
34 | return null;
35 | }
36 | for (; idx < list.length; idx++) {
37 | value = func(value, list[idx]);
38 | }
39 | return value;
40 | },
41 | __ntuplecomp: function (a, b) {
42 | var mlen = Math.max(a.length, b.length);
43 | for (var i = 0; i < mlen; i++) {
44 | if (a[i] < b[i]) return -1;
45 | if (a[i] > b[i]) return 1;
46 | }
47 | return a.length == b.length ? 0 : (a.length < b.length ? -1 : 1);
48 | },
49 | __calculate_ratio: function (matches, length) {
50 | return length ? 2.0 * matches / length : 1.0;
51 | },
52 | __isindict: function (dict) {
53 | return function (key) {
54 | return key in dict;
55 | };
56 | },
57 | __dictget: function (dict, key, defaultValue) {
58 | return key in dict ? dict[key] : defaultValue;
59 | },
60 | SequenceMatcher: function (a, b, isjunk) {
61 | this.set_seqs = function (a, b) {
62 | this.set_seq1(a);
63 | this.set_seq2(b);
64 | }
65 | this.set_seq1 = function (a) {
66 | if (a == this.a) return;
67 | this.a = a;
68 | this.matching_blocks = this.opcodes = null;
69 | }
70 | this.set_seq2 = function (b) {
71 | if (b == this.b) return;
72 | this.b = b;
73 | this.matching_blocks = this.opcodes = this.fullbcount = null;
74 | this.__chain_b();
75 | }
76 | this.__chain_b = function () {
77 | var b = this.b;
78 | var n = b.length;
79 | var b2j = this.b2j = {};
80 | var populardict = {};
81 | for (var i = 0; i < b.length; i++) {
82 | var elt = b[i];
83 | if (elt in b2j) {
84 | var indices = b2j[elt];
85 | if (n >= 200 && indices.length * 100 > n) {
86 | populardict[elt] = 1;
87 | delete b2j[elt];
88 | } else {
89 | indices.push(i);
90 | }
91 | } else {
92 | b2j[elt] = [i];
93 | }
94 | }
95 | for (var elt in populardict)
96 | delete b2j[elt];
97 | var isjunk = this.isjunk;
98 | var junkdict = {};
99 | if (isjunk) {
100 | for (var elt in populardict) {
101 | if (isjunk(elt)) {
102 | junkdict[elt] = 1;
103 | delete populardict[elt];
104 | }
105 | }
106 | for (var elt in b2j) {
107 | if (isjunk(elt)) {
108 | junkdict[elt] = 1;
109 | delete b2j[elt];
110 | }
111 | }
112 | }
113 | this.isbjunk = difflib.__isindict(junkdict);
114 | this.isbpopular = difflib.__isindict(populardict);
115 | }
116 | this.find_longest_match = function (alo, ahi, blo, bhi) {
117 | var a = this.a;
118 | var b = this.b;
119 | var b2j = this.b2j;
120 | var isbjunk = this.isbjunk;
121 | var besti = alo;
122 | var bestj = blo;
123 | var bestsize = 0;
124 | var j = null;
125 | var j2len = {};
126 | var nothing = [];
127 | for (var i = alo; i < ahi; i++) {
128 | var newj2len = {};
129 | var jdict = difflib.__dictget(b2j, a[i], nothing);
130 | for (var jkey in jdict) {
131 | j = jdict[jkey];
132 | if (j < blo) continue;
133 | if (j >= bhi) break;
134 | var k;
135 | newj2len[j] = k = difflib.__dictget(j2len, j - 1, 0) + 1;
136 | if (k > bestsize) {
137 | besti = i - k + 1;
138 | bestj = j - k + 1;
139 | bestsize = k;
140 | }
141 | }
142 | j2len = newj2len;
143 | }
144 | while (besti > alo && bestj > blo && !isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
145 | besti--;
146 | bestj--;
147 | bestsize++;
148 | }
149 | while (besti + bestsize < ahi && bestj + bestsize < bhi && !isbjunk(b[bestj + bestsize]) && a[besti + bestsize] == b[bestj + bestsize]) {
150 | bestsize++;
151 | }
152 | while (besti > alo && bestj > blo && isbjunk(b[bestj - 1]) && a[besti - 1] == b[bestj - 1]) {
153 | besti--;
154 | bestj--;
155 | bestsize++;
156 | }
157 | while (besti + bestsize < ahi && bestj + bestsize < bhi && isbjunk(b[bestj + bestsize]) && a[besti + bestsize] == b[bestj + bestsize]) {
158 | bestsize++;
159 | }
160 | return [besti, bestj, bestsize];
161 | }
162 | this.get_matching_blocks = function () {
163 | if (this.matching_blocks != null) return this.matching_blocks;
164 | var la = this.a.length;
165 | var lb = this.b.length;
166 | var queue = [
167 | [0, la, 0, lb]
168 | ];
169 | var matching_blocks = [];
170 | var alo, ahi, blo, bhi, qi, i, j, k, x;
171 | while (queue.length) {
172 | qi = queue.pop();
173 | alo = qi[0];
174 | ahi = qi[1];
175 | blo = qi[2];
176 | bhi = qi[3];
177 | x = this.find_longest_match(alo, ahi, blo, bhi);
178 | i = x[0];
179 | j = x[1];
180 | k = x[2];
181 | if (k) {
182 | matching_blocks.push(x);
183 | if (alo < i && blo < j) queue.push([alo, i, blo, j]);
184 | if (i + k < ahi && j + k < bhi) queue.push([i + k, ahi, j + k, bhi]);
185 | }
186 | }
187 | matching_blocks.sort(difflib.__ntuplecomp);
188 | var i1 = 0;
189 | var j1 = 0;
190 | var k1 = 0;
191 | var block = 0;
192 | var non_adjacent = [];
193 | for (var idx in matching_blocks) {
194 | block = matching_blocks[idx];
195 | var i2 = block[0];
196 | var j2 = block[1];
197 | var k2 = block[2];
198 | if (i1 + k1 == i2 && j1 + k1 == j2) {
199 | k1 += k2;
200 | } else {
201 | if (k1) non_adjacent.push([i1, j1, k1]);
202 | i1 = i2;
203 | j1 = j2;
204 | k1 = k2;
205 | }
206 | }
207 | if (k1) non_adjacent.push([i1, j1, k1]);
208 | non_adjacent.push([la, lb, 0]);
209 | this.matching_blocks = non_adjacent;
210 | return this.matching_blocks;
211 | }
212 | this.get_opcodes = function () {
213 | if (this.opcodes != null) return this.opcodes;
214 | var i = 0;
215 | var j = 0;
216 | var answer = [];
217 | this.opcodes = answer;
218 | var block, ai, bj, size, tag;
219 | var blocks = this.get_matching_blocks();
220 | for (var idx in blocks) {
221 | block = blocks[idx];
222 | ai = block[0];
223 | bj = block[1];
224 | size = block[2];
225 | tag = '';
226 | if (i < ai && j < bj) {
227 | tag = 'replace';
228 | } else if (i < ai) {
229 | tag = 'delete';
230 | } else if (j < bj) {
231 | tag = 'insert';
232 | }
233 | if (tag) answer.push([tag, i, ai, j, bj]);
234 | i = ai + size;
235 | j = bj + size;
236 | if (size) answer.push(['equal', ai, i, bj, j]);
237 | }
238 | return answer;
239 | }
240 | this.get_grouped_opcodes = function (n) {
241 | if (!n) n = 3;
242 | var codes = this.get_opcodes();
243 | if (!codes) codes = [
244 | ["equal", 0, 1, 0, 1]
245 | ];
246 | var code, tag, i1, i2, j1, j2;
247 | if (codes[0][0] == 'equal') {
248 | code = codes[0];
249 | tag = code[0];
250 | i1 = code[1];
251 | i2 = code[2];
252 | j1 = code[3];
253 | j2 = code[4];
254 | codes[0] = [tag, Math.max(i1, i2 - n), i2, Math.max(j1, j2 - n), j2];
255 | }
256 | if (codes[codes.length - 1][0] == 'equal') {
257 | code = codes[codes.length - 1];
258 | tag = code[0];
259 | i1 = code[1];
260 | i2 = code[2];
261 | j1 = code[3];
262 | j2 = code[4];
263 | codes[codes.length - 1] = [tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)];
264 | }
265 | var nn = n + n;
266 | var groups = [];
267 | for (var idx in codes) {
268 | code = codes[idx];
269 | tag = code[0];
270 | i1 = code[1];
271 | i2 = code[2];
272 | j1 = code[3];
273 | j2 = code[4];
274 | if (tag == 'equal' && i2 - i1 > nn) {
275 | groups.push([tag, i1, Math.min(i2, i1 + n), j1, Math.min(j2, j1 + n)]);
276 | i1 = Math.max(i1, i2 - n);
277 | j1 = Math.max(j1, j2 - n);
278 | }
279 | groups.push([tag, i1, i2, j1, j2]);
280 | }
281 | if (groups && groups[groups.length - 1][0] == 'equal') groups.pop();
282 | return groups;
283 | }
284 | this.ratio = function () {
285 | matches = difflib.__reduce(function (sum, triple) {
286 | return sum + triple[triple.length - 1];
287 | }, this.get_matching_blocks(), 0);
288 | return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
289 | }
290 | this.quick_ratio = function () {
291 | var fullbcount, elt;
292 | if (this.fullbcount == null) {
293 | this.fullbcount = fullbcount = {};
294 | for (var i = 0; i < this.b.length; i++) {
295 | elt = this.b[i];
296 | fullbcount[elt] = difflib.__dictget(fullbcount, elt, 0) + 1;
297 | }
298 | }
299 | fullbcount = this.fullbcount;
300 | var avail = {};
301 | var availhas = difflib.__isindict(avail);
302 | var matches = numb = 0;
303 | for (var i = 0; i < this.a.length; i++) {
304 | elt = this.a[i];
305 | if (availhas(elt)) {
306 | numb = avail[elt];
307 | } else {
308 | numb = difflib.__dictget(fullbcount, elt, 0);
309 | }
310 | avail[elt] = numb - 1;
311 | if (numb > 0) matches++;
312 | }
313 | return difflib.__calculate_ratio(matches, this.a.length + this.b.length);
314 | }
315 | this.real_quick_ratio = function () {
316 | var la = this.a.length;
317 | var lb = this.b.length;
318 | return _calculate_ratio(Math.min(la, lb), la + lb);
319 | }
320 | this.isjunk = isjunk ? isjunk : difflib.defaultJunkFunction;
321 | this.a = this.b = null;
322 | this.set_seqs(a, b);
323 | }
324 | }
325 |
326 | export default difflib;
327 |
--------------------------------------------------------------------------------
/src/renderer/utils/diffview.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | let diffview = {
3 | buildView: function (params) {
4 | var baseTextLines = params.baseTextLines;
5 | var newTextLines = params.newTextLines;
6 | var opcodes = params.opcodes;
7 | var baseTextName = params.baseTextName ? params.baseTextName : "Base Text";
8 | var newTextName = params.newTextName ? params.newTextName : "New Text";
9 | var contextSize = params.contextSize;
10 | var inline = (params.viewType == 0 || params.viewType == 1) ? params.viewType : 0;
11 | if (baseTextLines == null) throw "Cannot build diff view; baseTextLines is not defined.";
12 | if (newTextLines == null) throw "Cannot build diff view; newTextLines is not defined.";
13 | if (!opcodes) throw "Canno build diff view; opcodes is not defined.";
14 |
15 | function celt(name, clazz) {
16 | var e = document.createElement(name);
17 | e.className = clazz;
18 | return e;
19 | }
20 | function telt(name, text) {
21 | var e = document.createElement(name);
22 | e.appendChild(document.createTextNode(text));
23 | return e;
24 | }
25 | function ctelt(name, clazz, text) {
26 | var e = document.createElement(name);
27 | e.className = clazz;
28 | e.appendChild(document.createTextNode(text));
29 | return e;
30 | }
31 | var tdata = document.createElement("thead");
32 | var node = document.createElement("tr");
33 | tdata.appendChild(node);
34 | if (inline) {
35 | node.appendChild(document.createElement("th"));
36 | node.appendChild(document.createElement("th"));
37 | node.appendChild(ctelt("th", "texttitle", baseTextName + " vs. " + newTextName));
38 | } else {
39 | node.appendChild(document.createElement("th"));
40 | node.appendChild(ctelt("th", "texttitle", baseTextName));
41 | node.appendChild(document.createElement("th"));
42 | node.appendChild(ctelt("th", "texttitle", newTextName));
43 | }
44 | tdata = [tdata];
45 | var rows = [];
46 | var node2;
47 |
48 | function addCells(row, tidx, tend, textLines, change) {
49 | if (tidx < tend) {
50 | row.appendChild(telt("th", (tidx + 1).toString()));
51 | row.appendChild(ctelt("td", change, textLines[tidx].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
52 | return tidx + 1;
53 | } else {
54 | row.appendChild(document.createElement("th"));
55 | row.appendChild(celt("td", "empty"));
56 | return tidx;
57 | }
58 | }
59 | function addCellsInline(row, tidx, tidx2, textLines, change) {
60 | row.appendChild(telt("th", tidx == null ? "" : (tidx + 1).toString()));
61 | row.appendChild(telt("th", tidx2 == null ? "" : (tidx2 + 1).toString()));
62 | row.appendChild(ctelt("td", change, textLines[tidx != null ? tidx : tidx2].replace(/\t/g, "\u00a0\u00a0\u00a0\u00a0")));
63 | }
64 | for (var idx = 0; idx < opcodes.length; idx++) {
65 | var code = opcodes[idx];
66 | var change = code[0];
67 | var b = code[1];
68 | var be = code[2];
69 | var n = code[3];
70 | var ne = code[4];
71 | var rowcnt = Math.max(be - b, ne - n);
72 | var toprows = [];
73 | var botrows = [];
74 | for (var i = 0; i < rowcnt; i++) {
75 | if (contextSize && opcodes.length > 1 && ((idx > 0 && i == contextSize) || (idx == 0 && i == 0)) && change == "equal") {
76 | var jump = rowcnt - ((idx == 0 ? 1 : 2) * contextSize);
77 | if (jump > 1) {
78 | toprows.push(node = document.createElement("tr"));
79 | b += jump;
80 | n += jump;
81 | i += jump - 1;
82 | node.appendChild(telt("th", "..."));
83 | if (!inline) node.appendChild(ctelt("td", "skip", ""));
84 | node.appendChild(telt("th", "..."));
85 | node.appendChild(ctelt("td", "skip", ""));
86 | if (idx + 1 == opcodes.length) {
87 | break;
88 | } else {
89 | continue;
90 | }
91 | }
92 | }
93 | toprows.push(node = document.createElement("tr"));
94 | if (inline) {
95 | if (change == "insert") {
96 | addCellsInline(node, null, n++, newTextLines, change);
97 | } else if (change == "replace") {
98 | botrows.push(node2 = document.createElement("tr"));
99 | if (b < be) addCellsInline(node, b++, null, baseTextLines, "delete");
100 | if (n < ne) addCellsInline(node2, null, n++, newTextLines, "insert");
101 | } else if (change == "delete") {
102 | addCellsInline(node, b++, null, baseTextLines, change);
103 | } else {
104 | addCellsInline(node, b++, n++, baseTextLines, change);
105 | }
106 | } else {
107 | b = addCells(node, b, be, baseTextLines, change);
108 | n = addCells(node, n, ne, newTextLines, change);
109 | }
110 | }
111 | for (var i = 0; i < toprows.length; i++) rows.push(toprows[i]);
112 | for (var i = 0; i < botrows.length; i++) rows.push(botrows[i]);
113 | }
114 | tdata.push(node = document.createElement("tbody"));
115 | for (var idx in rows) node.appendChild(rows[idx]);
116 | node = celt("table", "diff" + (inline ? " inlinediff" : ""));
117 | for (var idx in tdata) node.appendChild(tdata[idx]);
118 | return node;
119 | }
120 | }
121 |
122 | export default diffview;
123 |
--------------------------------------------------------------------------------
/src/renderer/utils/language.json:
--------------------------------------------------------------------------------
1 | [
2 | "javascript",
3 | "html",
4 | "css",
5 | "coffee"
6 | ]
7 |
--------------------------------------------------------------------------------
/static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/moonrailgun/devbox/d725131b59646f1899545107f1fc5637e87840d6/static/.gitkeep
--------------------------------------------------------------------------------
/static/ports.json:
--------------------------------------------------------------------------------
1 | {
2 | "21": "FTP服务",
3 | "22": "SSH服务",
4 | "23": "Telnet服务",
5 | "25": "SMTP服务",
6 | "53": "DNS服务",
7 | "80": "HTTP服务",
8 | "109": "POP3服务",
9 | "143": "IMAP服务",
10 | "161": "SNMP服务",
11 | "443": "HTTPS服务",
12 | "993": "IMAP服务",
13 | "1080": "SOCKS服务",
14 | "1433": "SQL Server服务",
15 | "1500": "RPC服务",
16 | "3306": "MySQL服务",
17 | "27017": "mongodb服务"
18 | }
19 |
--------------------------------------------------------------------------------
/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "assert": true,
7 | "expect": true,
8 | "should": true,
9 | "__static": true
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/test/e2e/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | // Set BABEL_ENV to use proper env config
4 | process.env.BABEL_ENV = 'test'
5 |
6 | // Enable use of ES6+ on required files
7 | require('babel-register')({
8 | ignore: /node_modules/
9 | })
10 |
11 | // Attach Chai APIs to global scope
12 | const { expect, should, assert } = require('chai')
13 | global.expect = expect
14 | global.should = should
15 | global.assert = assert
16 |
17 | // Require all JS files in `./specs` for Mocha to consume
18 | require('require-dir')('./specs')
19 |
--------------------------------------------------------------------------------
/test/e2e/specs/Launch.spec.js:
--------------------------------------------------------------------------------
1 | import utils from '../utils'
2 |
3 | describe('Launch', function () {
4 | beforeEach(utils.beforeEach)
5 | afterEach(utils.afterEach)
6 |
7 | it('shows the proper application title', function () {
8 | return this.app.client.getTitle()
9 | .then(title => {
10 | expect(title).to.equal('devbox')
11 | })
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/test/e2e/utils.js:
--------------------------------------------------------------------------------
1 | import electron from 'electron'
2 | import { Application } from 'spectron'
3 |
4 | export default {
5 | afterEach () {
6 | this.timeout(10000)
7 |
8 | if (this.app && this.app.isRunning()) {
9 | return this.app.stop()
10 | }
11 | },
12 | beforeEach () {
13 | this.timeout(10000)
14 | this.app = new Application({
15 | path: electron,
16 | args: ['dist/electron/main.js'],
17 | startTimeout: 10000,
18 | waitTimeout: 10000
19 | })
20 |
21 | return this.app.start()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/test/unit/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | Vue.config.devtools = false
3 | Vue.config.productionTip = false
4 |
5 | // require all test files (files that ends with .spec.js)
6 | const testsContext = require.context('./specs', true, /\.spec$/)
7 | testsContext.keys().forEach(testsContext)
8 |
9 | // require all src files except main.js for coverage.
10 | // you can also change this to match only the subset of files that
11 | // you want coverage for.
12 | const srcContext = require.context('../../src/renderer', true, /^\.\/(?!main(\.js)?$)/)
13 | srcContext.keys().forEach(srcContext)
14 |
--------------------------------------------------------------------------------
/test/unit/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const path = require('path')
4 | const merge = require('webpack-merge')
5 | const webpack = require('webpack')
6 |
7 | const baseConfig = require('../../.electron-vue/webpack.renderer.config')
8 | const projectRoot = path.resolve(__dirname, '../../src/renderer')
9 |
10 | // Set BABEL_ENV to use proper preset config
11 | process.env.BABEL_ENV = 'test'
12 |
13 | let webpackConfig = merge(baseConfig, {
14 | devtool: '#inline-source-map',
15 | plugins: [
16 | new webpack.DefinePlugin({
17 | 'process.env.NODE_ENV': '"testing"'
18 | })
19 | ]
20 | })
21 |
22 | // don't treat dependencies as externals
23 | delete webpackConfig.entry
24 | delete webpackConfig.externals
25 | delete webpackConfig.output.libraryTarget
26 |
27 | // apply vue option to apply isparta-loader on js
28 | webpackConfig.module.rules
29 | .find(rule => rule.use.loader === 'vue-loader').use.options.loaders.js = 'babel-loader'
30 |
31 | module.exports = config => {
32 | config.set({
33 | browsers: ['visibleElectron'],
34 | client: {
35 | useIframe: false
36 | },
37 | coverageReporter: {
38 | dir: './coverage',
39 | reporters: [
40 | { type: 'lcov', subdir: '.' },
41 | { type: 'text-summary' }
42 | ]
43 | },
44 | customLaunchers: {
45 | 'visibleElectron': {
46 | base: 'Electron',
47 | flags: ['--show']
48 | }
49 | },
50 | frameworks: ['mocha', 'chai'],
51 | files: ['./index.js'],
52 | preprocessors: {
53 | './index.js': ['webpack', 'sourcemap']
54 | },
55 | reporters: ['spec', 'coverage'],
56 | singleRun: true,
57 | webpack: webpackConfig,
58 | webpackMiddleware: {
59 | noInfo: true
60 | }
61 | })
62 | }
63 |
--------------------------------------------------------------------------------
/test/unit/specs/LandingPage.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import LandingPage from '@/components/LandingPage'
3 |
4 | describe('LandingPage.vue', () => {
5 | it('should render correct contents', () => {
6 | const vm = new Vue({
7 | el: document.createElement('div'),
8 | render: h => h(LandingPage)
9 | }).$mount()
10 |
11 | expect(vm.$el.querySelector('.title').textContent).to.contain('Welcome to your new project!')
12 | })
13 | })
14 |
--------------------------------------------------------------------------------