├── .babelrc ├── .electron-vue ├── build.config.js ├── build.js ├── dev-client.js ├── dev-runner.js ├── webpack.main.config.js ├── webpack.renderer.config.js └── webpack.web.config.js ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── README.md ├── README_CN.md ├── build └── icons │ ├── 256x256.png │ ├── icon.icns │ └── icon.ico ├── dist ├── electron │ └── .gitkeep └── web │ └── .gitkeep ├── package-lock.json ├── package.json ├── src ├── index.ejs ├── main │ ├── index.dev.js │ └── index.js └── renderer │ ├── App.vue │ ├── Sidebar.vue │ ├── Tabs.vue │ ├── assets │ ├── .gitkeep │ ├── IT-Tools-preview.gif │ ├── alipay.gif │ ├── logo.png │ └── wechatpay.gif │ ├── components │ ├── ColorBoxPage.vue │ ├── CronPage.vue │ ├── CronPage │ │ └── CronHelp.vue │ ├── Image64Page.vue │ ├── JsonPage.vue │ ├── JsonPage │ │ └── JsonResultHelp.vue │ ├── LandingPage.vue │ ├── LandingPage │ │ └── Welcome.vue │ ├── RegexPage.vue │ ├── RegexPage │ │ └── RegexHelp.vue │ ├── TimestampPage.vue │ └── TimestampPage │ │ └── TstampHelp.vue │ ├── i18n.js │ ├── lang │ ├── cn.js │ ├── en.js │ └── messages.js │ ├── main.js │ ├── router │ └── index.js │ └── store │ ├── index.js │ └── modules │ ├── Color.js │ ├── Cron.js │ ├── Image64.js │ ├── Json.js │ ├── Menu.js │ ├── Regex.js │ ├── TStamp.js │ └── index.js ├── static └── .gitkeep └── 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.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | /** 4 | * `electron-packager` options 5 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-electron-packager.html 6 | */ 7 | module.exports = { 8 | arch: 'x64', 9 | asar: true, 10 | dir: path.join(__dirname, '../'), 11 | icon: path.join(__dirname, '../build/icons/icon'), 12 | ignore: /(^\/(src|test|\.[a-z]+|README|yarn|static|dist\/web))|\.gitkeep/, 13 | out: path.join(__dirname, '../build'), 14 | overwrite: true, 15 | platform: process.env.BUILD_TARGET || 'all' 16 | } 17 | -------------------------------------------------------------------------------- /.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 packager = require('electron-packager') 9 | const webpack = require('webpack') 10 | const Multispinner = require('multispinner') 11 | 12 | const buildConfig = require('./build.config') 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-packager`')}\n`) 49 | bundleApp() 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 | config.mode = 'production' 76 | webpack(config, (err, stats) => { 77 | if (err) reject(err.stack || err) 78 | else if (stats.hasErrors()) { 79 | let err = '' 80 | 81 | stats.toString({ 82 | chunks: false, 83 | colors: true 84 | }) 85 | .split(/\r?\n/) 86 | .forEach(line => { 87 | err += ` ${line}\n` 88 | }) 89 | 90 | reject(err) 91 | } else { 92 | resolve(stats.toString({ 93 | chunks: false, 94 | colors: true 95 | })) 96 | } 97 | }) 98 | }) 99 | } 100 | 101 | function bundleApp () { 102 | buildConfig.mode = 'production' 103 | packager(buildConfig, (err, appPaths) => { 104 | if (err) { 105 | console.log(`\n${errorLog}${chalk.yellow('`electron-packager`')} says...\n`) 106 | console.log(err + '\n') 107 | } else { 108 | console.log(`\n${doneLog}\n`) 109 | } 110 | }) 111 | } 112 | 113 | function web () { 114 | del.sync(['dist/web/*', '!.gitkeep']) 115 | webConfig.mode = 'production' 116 | webpack(webConfig, (err, stats) => { 117 | if (err || stats.hasErrors()) console.log(err) 118 | 119 | console.log(stats.toString({ 120 | chunks: false, 121 | colors: true 122 | })) 123 | 124 | process.exit() 125 | }) 126 | } 127 | 128 | function greeting () { 129 | const cols = process.stdout.columns 130 | let text = '' 131 | 132 | if (cols > 85) text = 'lets-build' 133 | else if (cols > 60) text = 'lets-|build' 134 | else text = false 135 | 136 | if (text && !isCI) { 137 | say(text, { 138 | colors: ['yellow'], 139 | font: 'simple3d', 140 | space: false 141 | }) 142 | } else console.log(chalk.yellow.bold('\n lets-build')) 143 | console.log() 144 | } -------------------------------------------------------------------------------- /.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 | rendererConfig.mode = 'development' 45 | const compiler = webpack(rendererConfig) 46 | hotMiddleware = webpackHotMiddleware(compiler, { 47 | log: false, 48 | heartbeat: 2500 49 | }) 50 | 51 | compiler.hooks.compilation.tap('compilation', compilation => { 52 | compilation.hooks.htmlWebpackPluginAfterEmit.tapAsync('html-webpack-plugin-after-emit', (data, cb) => { 53 | hotMiddleware.publish({ action: 'reload' }) 54 | cb() 55 | }) 56 | }) 57 | 58 | compiler.hooks.done.tap('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 | mainConfig.mode = 'development' 84 | const compiler = webpack(mainConfig) 85 | 86 | compiler.hooks.watchRun.tapAsync('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 | var args = [ 118 | '--inspect=5858', 119 | path.join(__dirname, '../dist/electron/main.js') 120 | ] 121 | 122 | // detect yarn or npm and process commandline args accordingly 123 | if (process.env.npm_execpath.endsWith('yarn.js')) { 124 | args = args.concat(process.argv.slice(3)) 125 | } else if (process.env.npm_execpath.endsWith('npm-cli.js')) { 126 | args = args.concat(process.argv.slice(2)) 127 | } 128 | 129 | electronProcess = spawn(electron, args) 130 | 131 | electronProcess.stdout.on('data', data => { 132 | electronLog(data, 'blue') 133 | }) 134 | electronProcess.stderr.on('data', data => { 135 | electronLog(data, 'red') 136 | }) 137 | 138 | electronProcess.on('close', () => { 139 | if (!manualRestart) process.exit() 140 | }) 141 | } 142 | 143 | function electronLog (data, color) { 144 | let log = '' 145 | data = data.toString().split(/\r?\n/) 146 | data.forEach(line => { 147 | log += ` ${line}\n` 148 | }) 149 | if (/[0-9A-z]+/.test(log)) { 150 | console.log( 151 | chalk[color].bold('┏ Electron -------------------') + 152 | '\n\n' + 153 | log + 154 | chalk[color].bold('┗ ----------------------------') + 155 | '\n' 156 | ) 157 | } 158 | } 159 | 160 | function greeting () { 161 | const cols = process.stdout.columns 162 | let text = '' 163 | 164 | if (cols > 104) text = 'electron-vue' 165 | else if (cols > 76) text = 'electron-|vue' 166 | else text = false 167 | 168 | if (text) { 169 | say(text, { 170 | colors: ['yellow'], 171 | font: 'simple3d', 172 | space: false 173 | }) 174 | } else console.log(chalk.yellow.bold('\n electron-vue')) 175 | console.log(chalk.blue(' getting ready...') + '\n') 176 | } 177 | 178 | function init () { 179 | greeting() 180 | 181 | Promise.all([startRenderer(), startMain()]) 182 | .then(() => { 183 | startElectron() 184 | }) 185 | .catch(err => { 186 | console.error(err) 187 | }) 188 | } 189 | 190 | init() 191 | -------------------------------------------------------------------------------- /.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 MiniCssExtractPlugin = require('mini-css-extract-plugin') 12 | const HtmlWebpackPlugin = require('html-webpack-plugin') 13 | const { VueLoaderPlugin } = require('vue-loader') 14 | 15 | /** 16 | * List of node_modules to include in webpack bundle 17 | * 18 | * Required for specific packages like Vue UI libraries 19 | * that provide pure *.vue files that need compiling 20 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/webpack-configurations.html#white-listing-externals 21 | */ 22 | let whiteListedModules = ['vue'] 23 | 24 | let rendererConfig = { 25 | devtool: '#cheap-module-eval-source-map', 26 | entry: { 27 | renderer: path.join(__dirname, '../src/renderer/main.js') 28 | }, 29 | externals: [ 30 | ...Object.keys(dependencies || {}).filter(d => !whiteListedModules.includes(d)) 31 | ], 32 | module: { 33 | rules: [ 34 | { 35 | test: /\.(js|vue)$/, 36 | enforce: 'pre', 37 | exclude: /node_modules/, 38 | use: { 39 | loader: 'eslint-loader', 40 | options: { 41 | formatter: require('eslint-friendly-formatter') 42 | } 43 | } 44 | }, 45 | { 46 | test: /\.scss$/, 47 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 48 | }, 49 | { 50 | test: /\.sass$/, 51 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 52 | }, 53 | { 54 | test: /\.less$/, 55 | use: ['vue-style-loader', 'css-loader', 'less-loader'] 56 | }, 57 | { 58 | test: /\.css$/, 59 | use: ['vue-style-loader', 'css-loader'] 60 | }, 61 | { 62 | test: /\.html$/, 63 | use: 'vue-html-loader' 64 | }, 65 | { 66 | test: /\.js$/, 67 | use: 'babel-loader', 68 | exclude: /node_modules/ 69 | }, 70 | { 71 | test: /\.node$/, 72 | use: 'node-loader' 73 | }, 74 | { 75 | test: /\.vue$/, 76 | use: { 77 | loader: 'vue-loader', 78 | options: { 79 | extractCSS: process.env.NODE_ENV === 'production', 80 | loaders: { 81 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 82 | scss: 'vue-style-loader!css-loader!sass-loader', 83 | less: 'vue-style-loader!css-loader!less-loader' 84 | } 85 | } 86 | } 87 | }, 88 | { 89 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 90 | use: { 91 | loader: 'url-loader', 92 | query: { 93 | limit: 10000, 94 | name: 'imgs/[name]--[folder].[ext]' 95 | } 96 | } 97 | }, 98 | { 99 | test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, 100 | loader: 'url-loader', 101 | options: { 102 | limit: 10000, 103 | name: 'media/[name]--[folder].[ext]' 104 | } 105 | }, 106 | { 107 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 108 | use: { 109 | loader: 'url-loader', 110 | query: { 111 | limit: 10000, 112 | name: 'fonts/[name]--[folder].[ext]' 113 | } 114 | } 115 | } 116 | ] 117 | }, 118 | node: { 119 | __dirname: process.env.NODE_ENV !== 'production', 120 | __filename: process.env.NODE_ENV !== 'production' 121 | }, 122 | plugins: [ 123 | new VueLoaderPlugin(), 124 | new MiniCssExtractPlugin({filename: 'styles.css'}), 125 | new HtmlWebpackPlugin({ 126 | filename: 'index.html', 127 | template: path.resolve(__dirname, '../src/index.ejs'), 128 | minify: { 129 | collapseWhitespace: true, 130 | removeAttributeQuotes: true, 131 | removeComments: true 132 | }, 133 | nodeModules: process.env.NODE_ENV !== 'production' 134 | ? path.resolve(__dirname, '../node_modules') 135 | : false 136 | }), 137 | new webpack.HotModuleReplacementPlugin(), 138 | new webpack.NoEmitOnErrorsPlugin() 139 | ], 140 | output: { 141 | filename: '[name].js', 142 | libraryTarget: 'commonjs2', 143 | path: path.join(__dirname, '../dist/electron') 144 | }, 145 | resolve: { 146 | alias: { 147 | '@': path.join(__dirname, '../src/renderer'), 148 | 'vue$': 'vue/dist/vue.esm.js' 149 | }, 150 | extensions: ['.js', '.vue', '.json', '.css', '.node'] 151 | }, 152 | target: 'electron-renderer' 153 | } 154 | 155 | /** 156 | * Adjust rendererConfig for development settings 157 | */ 158 | if (process.env.NODE_ENV !== 'production') { 159 | rendererConfig.plugins.push( 160 | new webpack.DefinePlugin({ 161 | '__static': `"${path.join(__dirname, '../static').replace(/\\/g, '\\\\')}"` 162 | }) 163 | ) 164 | } 165 | 166 | /** 167 | * Adjust rendererConfig for production settings 168 | */ 169 | if (process.env.NODE_ENV === 'production') { 170 | rendererConfig.devtool = '' 171 | 172 | rendererConfig.plugins.push( 173 | new BabiliWebpackPlugin(), 174 | new CopyWebpackPlugin([ 175 | { 176 | from: path.join(__dirname, '../static'), 177 | to: path.join(__dirname, '../dist/electron/static'), 178 | ignore: ['.*'] 179 | } 180 | ]), 181 | new webpack.DefinePlugin({ 182 | 'process.env.NODE_ENV': '"production"' 183 | }), 184 | new webpack.LoaderOptionsPlugin({ 185 | minimize: true 186 | }) 187 | ) 188 | } 189 | 190 | module.exports = rendererConfig 191 | -------------------------------------------------------------------------------- /.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 MiniCssExtractPlugin = require('mini-css-extract-plugin') 11 | const HtmlWebpackPlugin = require('html-webpack-plugin') 12 | const { VueLoaderPlugin } = require('vue-loader') 13 | 14 | let webConfig = { 15 | devtool: '#cheap-module-eval-source-map', 16 | entry: { 17 | web: path.join(__dirname, '../src/renderer/main.js') 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.(js|vue)$/, 23 | enforce: 'pre', 24 | exclude: /node_modules/, 25 | use: { 26 | loader: 'eslint-loader', 27 | options: { 28 | formatter: require('eslint-friendly-formatter') 29 | } 30 | } 31 | }, 32 | { 33 | test: /\.scss$/, 34 | use: ['vue-style-loader', 'css-loader', 'sass-loader'] 35 | }, 36 | { 37 | test: /\.sass$/, 38 | use: ['vue-style-loader', 'css-loader', 'sass-loader?indentedSyntax'] 39 | }, 40 | { 41 | test: /\.less$/, 42 | use: ['vue-style-loader', 'css-loader', 'less-loader'] 43 | }, 44 | { 45 | test: /\.css$/, 46 | use: ['vue-style-loader', 'css-loader'] 47 | }, 48 | { 49 | test: /\.html$/, 50 | use: 'vue-html-loader' 51 | }, 52 | { 53 | test: /\.js$/, 54 | use: 'babel-loader', 55 | include: [ path.resolve(__dirname, '../src/renderer') ], 56 | exclude: /node_modules/ 57 | }, 58 | { 59 | test: /\.vue$/, 60 | use: { 61 | loader: 'vue-loader', 62 | options: { 63 | extractCSS: true, 64 | loaders: { 65 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 66 | scss: 'vue-style-loader!css-loader!sass-loader', 67 | less: 'vue-style-loader!css-loader!less-loader' 68 | } 69 | } 70 | } 71 | }, 72 | { 73 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 74 | use: { 75 | loader: 'url-loader', 76 | query: { 77 | limit: 10000, 78 | name: 'imgs/[name].[ext]' 79 | } 80 | } 81 | }, 82 | { 83 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 84 | use: { 85 | loader: 'url-loader', 86 | query: { 87 | limit: 10000, 88 | name: 'fonts/[name].[ext]' 89 | } 90 | } 91 | } 92 | ] 93 | }, 94 | plugins: [ 95 | new VueLoaderPlugin(), 96 | new MiniCssExtractPlugin({filename: 'styles.css'}), 97 | new HtmlWebpackPlugin({ 98 | filename: 'index.html', 99 | template: path.resolve(__dirname, '../src/index.ejs'), 100 | minify: { 101 | collapseWhitespace: true, 102 | removeAttributeQuotes: true, 103 | removeComments: true 104 | }, 105 | nodeModules: false 106 | }), 107 | new webpack.DefinePlugin({ 108 | 'process.env.IS_WEB': 'true' 109 | }), 110 | new webpack.HotModuleReplacementPlugin(), 111 | new webpack.NoEmitOnErrorsPlugin() 112 | ], 113 | output: { 114 | filename: '[name].js', 115 | path: path.join(__dirname, '../dist/web') 116 | }, 117 | resolve: { 118 | alias: { 119 | '@': path.join(__dirname, '../src/renderer'), 120 | 'vue$': 'vue/dist/vue.esm.js' 121 | }, 122 | extensions: ['.js', '.vue', '.json', '.css'] 123 | }, 124 | target: 'web' 125 | } 126 | 127 | /** 128 | * Adjust webConfig for production settings 129 | */ 130 | if (process.env.NODE_ENV === 'production') { 131 | webConfig.devtool = '' 132 | 133 | webConfig.plugins.push( 134 | new BabiliWebpackPlugin(), 135 | new CopyWebpackPlugin([ 136 | { 137 | from: path.join(__dirname, '../static'), 138 | to: path.join(__dirname, '../dist/web/static'), 139 | ignore: ['.*'] 140 | } 141 | ]), 142 | new webpack.DefinePlugin({ 143 | 'process.env.NODE_ENV': '"production"' 144 | }), 145 | new webpack.LoaderOptionsPlugin({ 146 | minimize: true 147 | }) 148 | ) 149 | } 150 | 151 | module.exports = webConfig 152 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | electron-vue 4 |
5 |
6 |
7 | 8 |

9 | A programing helper for developers built with Electron & Vue.js 🚀 10 |

11 | 12 |

13 | English | [简体中文] 14 |

15 | 16 | 17 | # it-tools 18 | 19 | > Help programers quickly solve problems. 20 | > To make life easy. 21 | 22 | Now this project including Regex Tool, Timestamp Converter, Color Box, Json Parser, Base64 Converter, Crontab Tool. 23 | 24 | ## Features 25 | 26 | - Regex Tool 27 | - Timestamp Converter 28 | - Color Box 29 | - JSON Parser 30 | - Base64 Converter 31 | - Crontab Tool 32 | 33 | ### Try It 34 | [Download](https://github.com/TsaiKoga/it-tools/releases) 35 | 36 | [Online Demo](https://tsaikoga.github.com/it-tools/web/index.html) 37 | 38 | 39 | ### Preview 40 | 41 | ![IT Tools](/src/renderer/assets/IT-Tools-preview.gif) 42 | 43 | ### Build Setup 44 | 45 | ``` bash 46 | # install dependencies 47 | npm install 48 | 49 | # serve with hot reload at localhost:9080 50 | npm run dev 51 | 52 | # build electron application for production 53 | npm run build 54 | 55 | # run unit & end-to-end tests 56 | npm test 57 | 58 | 59 | # lint all JS/Vue component files in `src/` 60 | npm run lint 61 | 62 | ``` 63 | 64 | ### Donation 65 | 66 | If this project help you reduce time to develop, you can give me a cup of coffee :) 67 |
68 | 69 | 70 | 72 | 73 | 75 | 76 | 77 | 78 | 79 | 80 | 81 |
71 | electron-vueelectron-vue 74 | PayPal Logo
Wechat PayAli PayPaypal
82 |
83 | 84 | --- 85 | 86 | This project was generated with [electron-vue](https://github.com/SimulatedGREG/electron-vue)@[8fae476](https://github.com/SimulatedGREG/electron-vue/tree/8fae4763e9d225d3691b627e83b9e09b56f6c935) 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). 87 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | electron-vue 4 |
5 |
6 |
7 | 8 |

9 | 这是由 Electron & Vue.js 编写的,为程序员服务的编程工具 🚀 10 |

11 | 12 |

13 | [English] | 简体中文 14 |

15 | 16 | 17 | # it-tools 18 | 19 | > 旨在帮助程序员快速开发的小工具. 20 | > 让生活更加美好. 21 | 22 | 这个工具目前包括“正则表达式”、“时间戳转化”、“颜色盒子”、“Json转化”、“Base64转化“、“定时任务工具” 23 | 24 | 25 | ### 特点 26 | 27 | - 正则表达式 28 | - 时间戳转化 29 | - 颜色盒子 30 | - JSON 转化 31 | - Base64 转化 32 | - 定时任务 33 | 34 | 35 | ### 试一下 36 | [下载桌面应用](https://github.com/TsaiKoga/it-tools/releases) 37 | 38 | [在线演示](https://tsaikoga.github.com/it-tools/web/index.html) 39 | 40 | 41 | ### 预览 42 | 43 | ![IT Tools](/src/renderer/assets/IT-Tools-preview.gif) 44 | 45 | ### 构建步骤 46 | 47 | ``` bash 48 | # 安装依赖 49 | npm install 50 | 51 | # serve with hot reload at localhost:9080 52 | npm run dev 53 | 54 | # build electron application for production 55 | npm run build 56 | 57 | # run unit & end-to-end tests 58 | npm test 59 | 60 | 61 | # lint all JS/Vue component files in `src/` 62 | npm run lint 63 | 64 | ``` 65 | 66 | 67 | ### 捐赠 68 | 69 | 70 | 71 | 如果觉您觉得项目对您有帮助,可否打赏我一杯咖啡喝: 72 |
73 | 74 | 75 | 77 | 78 | 80 | 81 | 82 | 83 | 84 | 85 | 86 |
76 | electron-vueelectron-vue 79 | PayPal Logo
微信支付宝贝宝
87 |
88 | 89 | 此项目由以下工具编写: 90 | [electron-vue](https://github.com/SimulatedGREG/electron-vue)@[8fae476](https://github.com/SimulatedGREG/electron-vue/tree/8fae4763e9d225d3691b627e83b9e09b56f6c935) 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). 91 | -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/build/icons/icon.icns -------------------------------------------------------------------------------- /build/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/build/icons/icon.ico -------------------------------------------------------------------------------- /dist/electron/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/dist/electron/.gitkeep -------------------------------------------------------------------------------- /dist/web/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/dist/web/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "IT-Tools", 3 | "productName": "IT Tools", 4 | "version": "1.5.1", 5 | "author": "TsaiKoga ", 6 | "description": "Help programers quickly solve problem.To be a life saver.", 7 | "license": "Apache-2.0", 8 | "main": "./dist/electron/main.js", 9 | "scripts": { 10 | "build": "node .electron-vue/build.js", 11 | "build:darwin": "cross-env BUILD_TARGET=darwin node .electron-vue/build.js", 12 | "build:linux": "cross-env BUILD_TARGET=linux node .electron-vue/build.js", 13 | "build:mas": "cross-env BUILD_TARGET=mas node .electron-vue/build.js", 14 | "build:win32": "cross-env BUILD_TARGET=win32 node .electron-vue/build.js", 15 | "build:clean": "cross-env BUILD_TARGET=clean node .electron-vue/build.js", 16 | "build:web": "cross-env BUILD_TARGET=web node .electron-vue/build.js", 17 | "dev": "node .electron-vue/dev-runner.js", 18 | "e2e": "npm run pack && mocha test/e2e", 19 | "lint": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter src test", 20 | "lint:fix": "eslint --ext .js,.vue -f ./node_modules/eslint-friendly-formatter --fix src test", 21 | "pack": "npm run pack:main && npm run pack:renderer", 22 | "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", 23 | "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", 24 | "test": "npm run unit && npm run e2e", 25 | "unit": "karma start test/unit/karma.conf.js", 26 | "postinstall": "npm run lint:fix" 27 | }, 28 | "dependencies": { 29 | "cron-parser": "^2.13.0", 30 | "moment": "^2.24.0", 31 | "node-sass": "^4.12.0", 32 | "vue": "^2.5.16", 33 | "vue-clipboard2": "^0.3.0", 34 | "vue-electron": "^1.0.6", 35 | "vue-i18n": "^8.10.0", 36 | "vue-router": "^3.0.1", 37 | "vuex": "^3.0.1", 38 | "vuex-electron": "^1.0.0" 39 | }, 40 | "devDependencies": { 41 | "ajv": "^6.5.0", 42 | "babel-core": "^6.26.3", 43 | "babel-eslint": "^8.2.3", 44 | "babel-loader": "^7.1.4", 45 | "babel-plugin-istanbul": "^5.1.1", 46 | "babel-plugin-transform-runtime": "^6.23.0", 47 | "babel-preset-env": "^1.7.0", 48 | "babel-preset-stage-0": "^6.24.1", 49 | "babel-register": "^6.26.0", 50 | "babili-webpack-plugin": "^0.1.2", 51 | "cfonts": "^2.1.2", 52 | "chai": "^4.1.2", 53 | "chalk": "^2.4.1", 54 | "copy-webpack-plugin": "^4.5.1", 55 | "cross-env": "^5.1.6", 56 | "css-loader": "^0.28.11", 57 | "del": "^3.0.0", 58 | "devtron": "^1.4.0", 59 | "electron": "^2.0.4", 60 | "electron-debug": "^1.5.0", 61 | "electron-devtools-installer": "^2.2.4", 62 | "electron-packager": "^13.1.0", 63 | "electron-rebuild": "^1.8.1", 64 | "eslint": "^4.19.1", 65 | "eslint-config-standard": "^11.0.0", 66 | "eslint-friendly-formatter": "^4.0.1", 67 | "eslint-loader": "^2.0.0", 68 | "eslint-plugin-html": "^4.0.3", 69 | "eslint-plugin-import": "^2.12.0", 70 | "eslint-plugin-node": "^6.0.1", 71 | "eslint-plugin-promise": "^3.8.0", 72 | "eslint-plugin-standard": "^3.1.0", 73 | "file-loader": "^1.1.11", 74 | "html-webpack-plugin": "^3.2.0", 75 | "inject-loader": "^4.0.1", 76 | "karma": "^4.3.0", 77 | "karma-chai": "^0.1.0", 78 | "karma-coverage": "^1.1.2", 79 | "karma-electron": "^6.0.0", 80 | "karma-mocha": "^1.3.0", 81 | "karma-sourcemap-loader": "^0.3.7", 82 | "karma-spec-reporter": "^0.0.32", 83 | "karma-webpack": "^3.0.0", 84 | "mini-css-extract-plugin": "0.4.0", 85 | "mocha": "^5.2.0", 86 | "multispinner": "^0.2.1", 87 | "node-loader": "^0.6.0", 88 | "require-dir": "^1.0.0", 89 | "sass-loader": "^7.0.3", 90 | "spectron": "^3.8.0", 91 | "style-loader": "^0.21.0", 92 | "url-loader": "^1.0.1", 93 | "vue-html-loader": "^1.2.4", 94 | "vue-loader": "^15.2.4", 95 | "vue-style-loader": "^4.1.0", 96 | "vue-template-compiler": "^2.5.16", 97 | "webpack": "^4.15.1", 98 | "webpack-cli": "^3.0.8", 99 | "webpack-dev-server": "^3.1.4", 100 | "webpack-hot-middleware": "^2.22.2", 101 | "webpack-merge": "^4.1.3" 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | it-tools 6 | <% if (htmlWebpackPlugin.options.nodeModules) { %> 7 | 8 | 11 | <% } %> 12 | 13 | 14 |
15 | 16 | <% if (!process.browser) { %> 17 | 20 | <% } %> 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /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 | // Install `electron-debug` with `devtron` 11 | require('electron-debug')({ showDevTools: true }) 12 | 13 | // Install `vue-devtools` 14 | require('electron').app.on('ready', () => { 15 | let installExtension = require('electron-devtools-installer') 16 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 17 | .then(() => {}) 18 | .catch(err => { 19 | console.log('Unable to install `vue-devtools`: \n', err) 20 | }) 21 | }) 22 | 23 | // Require `main` process to boot app 24 | require('./index') -------------------------------------------------------------------------------- /src/main/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import { app, BrowserWindow, Menu } from 'electron' 4 | 5 | /** 6 | * Set `__static` path to static files in production 7 | * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html 8 | */ 9 | if (process.env.NODE_ENV !== 'development') { 10 | global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\') 11 | } 12 | 13 | let mainWindow 14 | const winURL = process.env.NODE_ENV === 'development' 15 | ? `http://localhost:9080` 16 | : `file://${__dirname}/index.html` 17 | 18 | function createWindow () { 19 | /** 20 | * Initial window options 21 | */ 22 | mainWindow = new BrowserWindow({ 23 | height: 650, 24 | width: 1100, 25 | 'minHeight': 650, 26 | 'minWidth': 1100, 27 | useContentSize: true, 28 | titleBarStyle: 'hiddenInset' 29 | }) 30 | 31 | mainWindow.loadURL(winURL) 32 | 33 | mainWindow.on('closed', () => { 34 | mainWindow = null 35 | }) 36 | 37 | const template = [{ 38 | label: 'Application', 39 | submenu: [ 40 | { label: 'About Application', selector: 'orderFrontStandardAboutPanel:' }, 41 | { type: 'separator' }, 42 | { label: 'Quit', accelerator: 'Command+Q', click: function () { app.quit() } } 43 | ]}, { 44 | label: 'Edit', 45 | submenu: [ 46 | { label: 'Undo', accelerator: 'CmdOrCtrl+Z', selector: 'undo:' }, 47 | { label: 'Redo', accelerator: 'Shift+CmdOrCtrl+Z', selector: 'redo:' }, 48 | { type: 'separator' }, 49 | { label: 'Cut', accelerator: 'CmdOrCtrl+X', selector: 'cut:' }, 50 | { label: 'Copy', accelerator: 'CmdOrCtrl+C', selector: 'copy:' }, 51 | { label: 'Paste', accelerator: 'CmdOrCtrl+V', selector: 'paste:' }, 52 | { label: 'Select All', accelerator: 'CmdOrCtrl+A', selector: 'selectAll:' } 53 | ]} 54 | ] 55 | 56 | Menu.setApplicationMenu(Menu.buildFromTemplate(template)) 57 | } 58 | 59 | app.on('ready', createWindow) 60 | 61 | app.on('window-all-closed', () => { 62 | if (process.platform !== 'darwin') { 63 | app.quit() 64 | } 65 | }) 66 | 67 | app.on('activate', () => { 68 | if (mainWindow === null) { 69 | createWindow() 70 | } 71 | }) 72 | -------------------------------------------------------------------------------- /src/renderer/App.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | 28 | 43 | -------------------------------------------------------------------------------- /src/renderer/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 96 | 97 | 178 | -------------------------------------------------------------------------------- /src/renderer/Tabs.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 38 | 39 | 40 | 66 | -------------------------------------------------------------------------------- /src/renderer/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/src/renderer/assets/.gitkeep -------------------------------------------------------------------------------- /src/renderer/assets/IT-Tools-preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/src/renderer/assets/IT-Tools-preview.gif -------------------------------------------------------------------------------- /src/renderer/assets/alipay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/src/renderer/assets/alipay.gif -------------------------------------------------------------------------------- /src/renderer/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/src/renderer/assets/logo.png -------------------------------------------------------------------------------- /src/renderer/assets/wechatpay.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/src/renderer/assets/wechatpay.gif -------------------------------------------------------------------------------- /src/renderer/components/ColorBoxPage.vue: -------------------------------------------------------------------------------- 1 | 97 | 98 | 147 | 148 | 406 | -------------------------------------------------------------------------------- /src/renderer/components/CronPage.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 123 | 124 | 312 | -------------------------------------------------------------------------------- /src/renderer/components/CronPage/CronHelp.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 85 | 86 | 136 | -------------------------------------------------------------------------------- /src/renderer/components/Image64Page.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 70 | 71 | 194 | -------------------------------------------------------------------------------- /src/renderer/components/JsonPage.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 49 | 50 | 138 | -------------------------------------------------------------------------------- /src/renderer/components/JsonPage/JsonResultHelp.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 79 | 80 | 191 | -------------------------------------------------------------------------------- /src/renderer/components/LandingPage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /src/renderer/components/LandingPage/Welcome.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 20 | 21 | 89 | -------------------------------------------------------------------------------- /src/renderer/components/RegexPage.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 132 | 133 | 358 | -------------------------------------------------------------------------------- /src/renderer/components/RegexPage/RegexHelp.vue: -------------------------------------------------------------------------------- 1 | 146 | 147 | 152 | 153 | 193 | -------------------------------------------------------------------------------- /src/renderer/components/TimestampPage.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 129 | 130 | 353 | -------------------------------------------------------------------------------- /src/renderer/components/TimestampPage/TstampHelp.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 121 | 122 | 179 | -------------------------------------------------------------------------------- /src/renderer/i18n.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import { messages } from './lang/messages' 4 | 5 | Vue.use(VueI18n) 6 | if (!window.localStorage.getItem('locale')) { 7 | window.localStorage.setItem('locale', 'en') 8 | } 9 | 10 | const i18n = new VueI18n({ 11 | locale: window.localStorage.getItem('locale'), 12 | messages: messages 13 | }) 14 | 15 | export default i18n 16 | -------------------------------------------------------------------------------- /src/renderer/lang/cn.js: -------------------------------------------------------------------------------- 1 | export const cn = { 2 | tabs: { 3 | home: '首页', 4 | help: '帮助' 5 | }, 6 | // Sidebar 7 | sidebar: { 8 | regexTool: '正则表达式工具', 9 | timestampConverter: '时间戳转化', 10 | colorBox: '颜色盒子', 11 | jsonParser: 'JSON 转化', 12 | base64Converter: 'Base64 转化', 13 | cronTool: '定时任务工具', 14 | regex: '正', 15 | time: '时', 16 | color: '色', 17 | json: '键', 18 | image64: '图', 19 | cron: '定' 20 | }, 21 | // Regex Tool 22 | regex: { 23 | yourRegexExp: '您的正则表达式', 24 | yourTestStr: '需要测试的字符串', 25 | clearFields: '清除所有输入', 26 | matchResult: '匹配结果', 27 | noMatches: '没有匹配到内容 ...', 28 | matchGroups: '匹配所有分组', 29 | matchGroup: '匹配分组', 30 | cleanFields: '清除输入框', 31 | heresResult: '这里显示结果。', 32 | uHaveAnUnmatchedPats: '您有一个括号缺少一边。', 33 | expError: '表达式错误', 34 | invalidOpt: '非法选项' 35 | }, 36 | regexHelp: { 37 | title: '正则表达式速查表', 38 | squareBracket: '其中一个字符: a, b, 或 c', 39 | squareNot: '任意字符除了: a, b, 或 c', 40 | squareAny: 'a 到 z 中的任意字符', 41 | squareRange: 'a 到 z 或 A 到 Z 中的任意字符', 42 | startOfLine: '行开始符', 43 | endOfLine: '行结束符', 44 | startOfStr: '字符串开始符', 45 | endOfStr: '字符串结束符', 46 | anyChar: '任意一个字符', 47 | anyWSpace: '任意空白字符', 48 | anyNWSpace: '任意非空白字符', 49 | anyDigit: '任意数字', 50 | stopAtFirstA: '非贪婪:只在第一个 a 停止', 51 | matchBeforeTom: '匹配 Tom 前面的字符', 52 | 53 | anyNDigit: '任意非数字', 54 | anyWordChar: '任何词语字符 (字母, 数字, 下划线)', 55 | anyNWordChar: '任意非词语字符', 56 | anyWordB: '任意词语分界符', 57 | capEnclosed: '所有被括号包住的内容', 58 | or: 'a 或者 b', 59 | zeroOne: '0 或者 1 个 a', 60 | moreThanZero: '大于 0 个 a', 61 | moreThanOne: '大于 1 个 a', 62 | exactly3: '正好 3 个 a', 63 | moreThan3: '大于 3 个 a', 64 | between: '3 到 6 个 a', 65 | matchTom: '匹配 Tom', 66 | matchBehindTom: '匹配 Tom 后面的字符', 67 | 68 | options: '选项', 69 | caseI: '不区分大小写', 70 | unicode: '将规则做为 Unicode 序列', 71 | newLine: '允许用 . 匹配新行', 72 | indexMatch: '只匹配目标字符串中此正则表达式的 lastIndex 属性所指示的索引(不尝试匹配任何后续索引)' 73 | }, 74 | // Timestamp Converter 75 | timestamp: { 76 | unitTimeChosen: '选择时间单位', 77 | sec: '秒', 78 | microSec: '毫秒', 79 | reset: '重置', 80 | inputTimestamp: '输入时间戳', 81 | datetimeResult: '日期时间结果', 82 | timestampResult: '时间戳结果', 83 | inputDatetime: '输入日期时间' 84 | }, 85 | // Color box 86 | color: { 87 | currentColor: '当前颜色', 88 | copied: '已复制', 89 | inputHex: '输入十六进制', 90 | inputRGB: '输入 RGB', 91 | inputHSL: '输入 HSL' 92 | }, 93 | // Json Parser 94 | json: { 95 | error: '错误', 96 | toXML: '转为 XML', 97 | toJSON: '转为 JSON', 98 | reset: '重置', 99 | copy: '复制', 100 | seems: '貌似', 101 | missing: '没有 ', 102 | missingComma: '没有逗号', 103 | missingKV: '在逗号后面没有键值对.', 104 | missingElemInArray: '数组中缺少元素', 105 | missingCommaInArray: '数组中缺少逗号', 106 | undefinedVal: '键值对的值未定义', 107 | undefinedKey: '键值对的键未定义.', 108 | near: '错误发生在', 109 | notBool: '错误: 非 Boolean 值', 110 | notNum: '非数值number...', 111 | notNull: '错误:非 null 值', 112 | moreBehind: '后面多了字符', 113 | inputJsonTip: '此处输入 JSON 字符串\n\n注意: 键值对要用双引号括起来' 114 | }, 115 | // Image Converter 116 | image64: { 117 | copy: '复制', 118 | clean: '清除', 119 | dragImgHere: '拖动 😺 图片到此处,Base64 👉', 120 | inputBase64Here: '您也可以在这里输入 Base64 码,图片将会显示 👈' 121 | }, 122 | cron: { 123 | yourCronExp: '您的定时任务表达式', 124 | heresResult: '这里显示结果', 125 | expError: '表达式错误', 126 | nextExec: '接下来执行如下的', 127 | times: '次', 128 | every: '每', 129 | at: '在', 130 | in: '在', 131 | on: '在', 132 | from: '从', 133 | through: '到', 134 | and: '和', 135 | of: '的' 136 | }, 137 | cronHelp: { 138 | minute: '分钟', 139 | hour: '小时', 140 | day: '天', 141 | month: '月', 142 | year: '年', 143 | week: '周', 144 | dayOfWeekUnit: '周[按周区分的天]', 145 | dayOfMonthUnit: '号[按月区分的天]', 146 | dayOfMonth: '号', 147 | dayOfWeek: '周', 148 | sunday: '周日', 149 | monday: '周一', 150 | tuesday: '周二', 151 | wednesday: '周三', 152 | thursday: '周四', 153 | friday: '周五', 154 | saturday: '周六', 155 | yearly: '每年', 156 | annually: '每年一次', 157 | monthly: '每月', 158 | daily: '每天', 159 | hourly: '每小时', 160 | afterReboot: '重启后', 161 | optional: '可选', 162 | anyValue: '任一值', 163 | valueListSeparator: '分隔开的值', 164 | rangeOfValues: '范围内的值', 165 | stepValues: '阶步值', 166 | allowedValues: '允许的值', 167 | alternativeSingleValues: '可变的值', 168 | nonStandard: '非标准的', 169 | notWorkEveryCron: '并非所有定时任务都能生效', 170 | expNotComplete: '表达式不完善' 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/renderer/lang/en.js: -------------------------------------------------------------------------------- 1 | export const en = { 2 | tabs: { 3 | home: 'Home', 4 | help: 'Help' 5 | }, 6 | // Sidebar 7 | sidebar: { 8 | regexTool: 'Regex Tool', 9 | timestampConverter: 'Timestamp Converter', 10 | colorBox: 'Color Box', 11 | jsonParser: 'Json Parser', 12 | base64Converter: 'Base64 Converter', 13 | cronTool: 'Crontab Tool', 14 | regex: 'Regex', 15 | time: 'Time', 16 | color: 'Color', 17 | json: 'Json', 18 | image64: 'Image', 19 | cron: 'Cron' 20 | }, 21 | // Regex Tool 22 | regex: { 23 | yourRegexExp: 'Your regular expression', 24 | yourTestStr: 'Your test string', 25 | clearFields: 'Clear Fields', 26 | matchResult: 'Match Result', 27 | noMatches: 'No matches ...', 28 | matchGroups: 'Match Groups', 29 | matchGroup: 'Match Group', 30 | cleanFields: 'Clean Fields', 31 | heresResult: 'Here\'s result.', 32 | uHaveAnUnmatchedPats: 'You have an unmatched parenthesis.', 33 | expError: 'Expression Error', 34 | invalidOpt: 'Invalid Option' 35 | }, 36 | regexHelp: { 37 | title: 'Regex quick reference', 38 | squareBracket: 'A single character of: a, b, or c', 39 | squareNot: 'Any single character except: a, b, or c', 40 | squareAny: 'Any single character in the range a-z', 41 | squareRange: 'Any single character in the range a-z or A-Z', 42 | startOfLine: 'Start of line', 43 | endOfLine: 'End of line', 44 | startOfStr: 'Start of string', 45 | endOfStr: 'End of string', 46 | anyChar: 'Any single character', 47 | anyWSpace: 'Any whitespace character', 48 | anyNWSpace: 'Any non-whitespace character', 49 | anyDigit: 'Any digit', 50 | stopAtFirstA: 'Not greedy: Stop at first a', 51 | matchBeforeTom: 'Match char before Tom', 52 | 53 | anyNDigit: 'Any non-digit', 54 | anyWordChar: 'Any word character (letter, number, underscore)', 55 | anyNWordChar: 'Any non-word character', 56 | anyWordB: 'Any word boundary', 57 | capEnclosed: 'Capture everything enclosed', 58 | or: 'a or b', 59 | zeroOne: 'Zero or one of a', 60 | moreThanZero: 'Zero or more of a', 61 | moreThanOne: 'One or more of a', 62 | exactly3: 'Exactly 3 of a', 63 | moreThan3: '3 or more of a', 64 | between: 'Between 3 and 6 of a', 65 | matchTom: 'Only match Tom', 66 | matchBehindTom: 'Match char behind Tom', 67 | 68 | options: 'Options', 69 | caseI: 'case insensitive', 70 | unicode: 'treat pattern as a sequence of Unicode code points', 71 | newLine: 'allows . to match newlines', 72 | indexMatch: 'matches only from the index indicated by the lastIndex property of this regular expression in the target string (and does not attempt to match from any later indexes)' 73 | }, 74 | // Timestamp Converter 75 | timestamp: { 76 | unitTimeChosen: 'Choose the unit of time', 77 | sec: 'Second', 78 | microSec: 'Micro Second', 79 | reset: 'Reset', 80 | inputTimestamp: 'Input timestamp', 81 | datetimeResult: 'Datetime Result', 82 | timestampResult: 'Timestamp Result', 83 | inputDatetime: 'Input Datetime' 84 | }, 85 | // Color box 86 | color: { 87 | currentColor: 'Current Color', 88 | copied: 'Copied', 89 | inputHex: 'Input Hex', 90 | inputRGB: 'Input RGB', 91 | inputHSL: 'Input HSL' 92 | }, 93 | // Json Parser 94 | json: { 95 | error: 'Error', 96 | toXML: 'TO XML', 97 | toJSON: 'TO JSON', 98 | reset: 'Reset', 99 | copy: 'Copy', 100 | ps: 'PS:The Key Value must be enclosed in double quotation marks.', 101 | seems: 'It seems that ', 102 | missing: 'missing', 103 | missingComma: 'missing comma', 104 | missingKV: 'missing key-value after comma.', 105 | missingElemInArray: 'missing element in array.', 106 | missingCommaInArray: 'missing comma in array.', 107 | undefinedVal: 'the value is undefined.', 108 | undefinedKey: 'the Key is undefined.', 109 | near: 'This error occured near', 110 | notBool: 'Error: It\'s not Boolean', 111 | notNull: 'Error: It\'s not null', 112 | notNum: 'it\'s not number...', 113 | moreBehind: 'More character in the end', 114 | inputJsonTip: 'Input your Json here.\n\nPS:The Key Value must be enclosed in double quotation marks.' 115 | }, 116 | // Image Converter 117 | image64: { 118 | copy: 'COPY', 119 | clean: 'CLEAN', 120 | dragImgHere: 'Drag your 😺 image here. Base64 👉', 121 | inputBase64Here: 'You can input Base64 code here, and the image will be shown 👈.' 122 | }, 123 | // crontab 124 | cron: { 125 | yourCronExp: 'Your Crontab expression', 126 | heresResult: 'Here\'s result.', 127 | expError: 'Expression error.', 128 | nextExec: 'Execute like as follow next ', 129 | times: 'times', 130 | every: 'every', 131 | at: 'At', 132 | in: 'In', 133 | on: 'On', 134 | from: 'from', 135 | through: 'through', 136 | and: 'and', 137 | of: 'of' 138 | }, 139 | cronHelp: { 140 | minute: 'minute', 141 | hour: 'hour', 142 | day: 'day', 143 | month: 'month', 144 | year: 'year', 145 | week: 'week', 146 | optional: 'optional', 147 | dayOfWeekUnit: 'day-of-month', 148 | dayOfMonthUnit: 'day-of-month', 149 | dayOfMonth: 'day-of-month', 150 | dayOfWeek: 'day-of-week', 151 | sunday: 'Sunday', 152 | monday: 'Monday', 153 | tuesday: 'Tuesday', 154 | wednesday: 'Wensday', 155 | thursday: 'Thursday', 156 | friday: 'Friday', 157 | saturday: 'Saturday', 158 | yearly: 'Yearly', 159 | annually: 'Annually', 160 | monthly: 'Monthly', 161 | daily: 'Daily', 162 | hourly: 'Hourly', 163 | afterReboot: 'After rebooting', 164 | anyValue: 'any value', 165 | valueListSeparator: 'value list separator', 166 | rangeOfValues: 'range of values', 167 | stepValues: 'step values', 168 | allowedValues: 'allowed values', 169 | alternativeSingleValues: 'alternative single values', 170 | nonStandard: 'non-standard', 171 | notWorkEveryCron: 'May not work with every cron', 172 | expNotComplete: 'The expression is not completed' 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/renderer/lang/messages.js: -------------------------------------------------------------------------------- 1 | import { cn } from './cn' 2 | import { en } from './en' 3 | 4 | export const messages = { 5 | en: en, 6 | cn: cn 7 | } 8 | -------------------------------------------------------------------------------- /src/renderer/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueClipboard from 'vue-clipboard2' 3 | 4 | import App from './App' 5 | import router from './router' 6 | import store from './store' 7 | import i18n from './i18n' 8 | 9 | if (!process.env.IS_WEB) Vue.use(require('vue-electron')) 10 | Vue.config.productionTip = false 11 | Vue.use(VueClipboard) 12 | 13 | /* eslint-disable no-new */ 14 | new Vue({ 15 | components: { App }, 16 | router, 17 | store, 18 | i18n, 19 | template: '' 20 | }).$mount('#app') 21 | -------------------------------------------------------------------------------- /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: '/regex-page', 15 | name: 'regex-page', 16 | component: require('@/components/RegexPage').default 17 | }, 18 | { 19 | path: '/timestamp-page', 20 | name: 'timestamp-page', 21 | component: require('@/components/TimestampPage').default 22 | }, 23 | { 24 | path: '/color-box-page', 25 | name: 'color-box-page', 26 | component: require('@/components/ColorBoxPage').default 27 | }, 28 | { 29 | path: '/json-page', 30 | name: 'json-page', 31 | component: require('@/components/JsonPage').default 32 | }, 33 | { 34 | path: '/image64-page', 35 | name: 'image64-page', 36 | component: require('@/components/Image64Page').default 37 | }, 38 | { 39 | path: '/cron-page', 40 | name: 'cron-page', 41 | component: require('@/components/CronPage').default 42 | } 43 | ] 44 | }) 45 | -------------------------------------------------------------------------------- /src/renderer/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | // import { createPersistedState, createSharedMutations } from 'vuex-electron' 5 | // import { createPersistedState } from 'vuex-electron' 6 | 7 | import modules from './modules' 8 | 9 | Vue.use(Vuex) 10 | 11 | export default new Vuex.Store({ 12 | modules, 13 | plugins: [ 14 | // createPersistedState(), 15 | // createSharedMutations() 16 | // createPersistedState() 17 | ], 18 | strict: process.env.NODE_ENV !== 'production' 19 | }) 20 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Color.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | colorMatrix: [ 3 | [{ hex: '#F9EBEA', rgb: '249,235,234', hsl: '6,54%,95%', anim: false, name: 'pomegranate' }, 4 | { hex: '#F2D7D5', rgb: '242,215,213', hsl: '6,54%,89%', anim: false, name: 'pomegranate' }, 5 | { hex: '#E6B0AA', rgb: '230,176,170', hsl: '6,54%,78%', anim: false, name: 'pomegranate' }, 6 | { hex: '#D98880', rgb: '217,136,128', hsl: '6,54%,68%', anim: false, name: 'pomegranate' }, 7 | { hex: '#CD6155', rgb: '205,97,85', hsl: '6,54%,57%', anim: false, name: 'pomegranate' }, 8 | { hex: '#C0392B', rgb: '192,57,43', hsl: '6,63%,46%', anim: false, name: 'pomegranate' }, 9 | { hex: '#A93226', rgb: '169,50,38', hsl: '6,63%,41%', anim: false, name: 'pomegranate' }, 10 | { hex: '#922B21', rgb: '146,43,33', hsl: '6,63%,35%', anim: false, name: 'pomegranate' }, 11 | { hex: '#7B241C', rgb: '123,36,28', hsl: '6,63%,29%', anim: false, name: 'pomegranate' }, 12 | { hex: '#641E16', rgb: '100, 30, 22', hsl: '6, 63%, 24%', anim: false, name: 'pomegranate' }], 13 | 14 | [{ hex: '#FDEDEC', rgb: '253,237,236', hsl: '6,78%,96%', anim: false, name: 'alizarin' }, 15 | { hex: '#FADBD8', rgb: '250,219,216', hsl: '6,78%,91%', anim: false, name: 'alizarin' }, 16 | { hex: '#F5B7B1', rgb: '245,183,177', hsl: '6,78%,83%', anim: false, name: 'alizarin' }, 17 | { hex: '#F1948A', rgb: '241,148,138', hsl: '6,78%,74%', anim: false, name: 'alizarin' }, 18 | { hex: '#EC7063', rgb: '236,112,99', hsl: '6,78%,66%', anim: false, name: 'alizarin' }, 19 | { hex: '#E74C3C', rgb: '231,76,60', hsl: '6,78%,57%', anim: false, name: 'alizarin' }, 20 | { hex: '#CB4335', rgb: '203,67,53', hsl: '6,59%,50%', anim: false, name: 'alizarin' }, 21 | { hex: '#B03A2E', rgb: '176,58,46', hsl: '6,59%,43%', anim: false, name: 'alizarin' }, 22 | { hex: '#943126', rgb: '148,49,38', hsl: '6,59%,37%', anim: false, name: 'alizarin' }, 23 | { hex: '#78281F', rgb: '120,40,31', hsl: '6,59%,30%', anim: false, name: 'alizarin' }], 24 | 25 | [{ hex: '#F5EEF8', rgb: '245, 238, 248', hsl: '283, 39%, 95%', anim: false, name: 'amethyst' }, 26 | { hex: '#EBDEF0', rgb: '235, 222, 240', hsl: '283, 39%, 91%', anim: false, name: 'amethyst' }, 27 | { hex: '#D7BDE2', rgb: '215, 189, 226', hsl: '283, 39%, 81%', anim: false, name: 'amethyst' }, 28 | { hex: '#C39BD3', rgb: '195, 155, 211', hsl: '283, 39%, 72%', anim: false, name: 'amethyst' }, 29 | { hex: '#AF7AC5', rgb: '175, 122, 197', hsl: '283, 39%, 63%', anim: false, name: 'amethyst' }, 30 | { hex: '#9B59B6', rgb: '155, 89, 182', hsl: '283, 39%, 53%', anim: false, name: 'amethyst' }, 31 | { hex: '#884EA0', rgb: '136, 78, 160', hsl: '283, 34%, 47%', anim: false, name: 'amethyst' }, 32 | { hex: '#76448A', rgb: '118, 68, 138', hsl: '283, 34%, 40%', anim: false, name: 'amethyst' }, 33 | { hex: '#633974', rgb: '99, 57, 116', hsl: '283, 34%, 34%', anim: false, name: 'amethyst' }, 34 | { hex: '#512E5F', rgb: '81, 46, 95', hsl: '283, 34%, 28%', anim: false, name: 'amethyst' }], 35 | 36 | [{ hex: '#F4ECF7', rgb: '244, 236, 247', hsl: '282, 39%, 95%', anim: false, name: 'wisteria' }, 37 | { hex: '#E8DAEF', rgb: '232, 218, 239', hsl: '282, 39%, 89%', anim: false, name: 'wisteria' }, 38 | { hex: '#D2B4DE', rgb: '210, 180, 222', hsl: '282, 39%, 79%', anim: false, name: 'wisteria' }, 39 | { hex: '#BB8FCE', rgb: '187, 143, 206', hsl: '282, 39%, 68%', anim: false, name: 'wisteria' }, 40 | { hex: '#A569BD', rgb: '165, 105, 189', hsl: '282, 39%, 58%', anim: false, name: 'wisteria' }, 41 | { hex: '#8E44AD', rgb: '142, 68, 173', hsl: '282, 44%, 47%', anim: false, name: 'wisteria' }, 42 | { hex: '#7D3C98', rgb: '125, 60, 152', hsl: '282, 44%, 42%', anim: false, name: 'wisteria' }, 43 | { hex: '#6C3483', rgb: '108, 52, 131', hsl: '282, 44%, 36%', anim: false, name: 'wisteria' }, 44 | { hex: '#5B2C6F', rgb: '91, 44, 111', hsl: '282, 44%, 30%', anim: false, name: 'wisteria' }, 45 | { hex: '#4A235A', rgb: '74, 35, 90', hsl: '282, 44%, 25%', anim: false, name: 'wisteria' }], 46 | 47 | [{ hex: '#EAF2F8', rgb: '234, 242, 248', hsl: '204, 51%, 94%', anim: false, name: 'belize hole' }, 48 | { hex: '#D4E6F1', rgb: '212, 230, 241', hsl: '204, 51%, 89%', anim: false, name: 'belize hole' }, 49 | { hex: '#A9CCE3', rgb: '169, 204, 227', hsl: '204, 51%, 78%', anim: false, name: 'belize hole' }, 50 | { hex: '#7FB3D5', rgb: '127, 179, 213', hsl: '204, 51%, 67%', anim: false, name: 'belize hole' }, 51 | { hex: '#5499C7', rgb: '84, 153, 199', hsl: '204, 51%, 55%', anim: false, name: 'belize hole' }, 52 | { hex: '#2980B9', rgb: '41, 128, 185', hsl: '204, 64%, 44%', anim: false, name: 'belize hole' }, 53 | { hex: '#2471A3', rgb: '36, 113, 163', hsl: '204, 64%, 39%', anim: false, name: 'belize hole' }, 54 | { hex: '#1F618D', rgb: '31, 97, 141', hsl: '204, 64%, 34%', anim: false, name: 'belize hole' }, 55 | { hex: '#1A5276', rgb: '26, 82, 118', hsl: '204, 64%, 28%', anim: false, name: 'belize hole' }, 56 | { hex: '#154360', rgb: '21, 67, 96', hsl: '204, 64%, 23%', anim: false, name: 'belize hole' }], 57 | 58 | [{ hex: '#EBF5FB', rgb: '235, 245, 251', hsl: '204, 70%, 95%', anim: false, name: 'peter river' }, 59 | { hex: '#D6EAF8', rgb: '214, 234, 248', hsl: '204, 70%, 91%', anim: false, name: 'peter river' }, 60 | { hex: '#AED6F1', rgb: '174, 214, 241', hsl: '204, 70%, 81%', anim: false, name: 'peter river' }, 61 | { hex: '#85C1E9', rgb: '133, 193, 233', hsl: '204, 70%, 72%', anim: false, name: 'peter river' }, 62 | { hex: '#5DADE2', rgb: '93, 173, 226', hsl: '204, 70%, 63%', anim: false, name: 'peter river' }, 63 | { hex: '#3498DB', rgb: '52, 152, 219', hsl: '204, 70%, 53%', anim: false, name: 'peter river' }, 64 | { hex: '#2E86C1', rgb: '46, 134, 193', hsl: '204, 62%, 47%', anim: false, name: 'peter river' }, 65 | { hex: '#2874A6', rgb: '40, 116, 166', hsl: '204, 62%, 40%', anim: false, name: 'peter river' }, 66 | { hex: '#21618C', rgb: '33, 97, 140', hsl: '204, 62%, 34%', anim: false, name: 'peter river' }, 67 | { hex: '#1B4F72', rgb: '27, 79, 114', hsl: '204, 62%, 28%', anim: false, name: 'peter river' }], 68 | 69 | [{ hex: '#E8F8F5', rgb: '232, 248, 245', hsl: '168, 55%, 94%', anim: false, name: 'turquoise' }, 70 | { hex: '#D1F2EB', rgb: '209, 242, 235', hsl: '168, 55%, 88%', anim: false, name: 'turquoise' }, 71 | { hex: '#A3E4D7', rgb: '163, 228, 215', hsl: '168, 55%, 77%', anim: false, name: 'turquoise' }, 72 | { hex: '#76D7C4', rgb: '118, 215, 196', hsl: '168, 55%, 65%', anim: false, name: 'turquoise' }, 73 | { hex: '#48C9B0', rgb: '72, 201, 176', hsl: '168, 55%, 54%', anim: false, name: 'turquoise' }, 74 | { hex: '#1ABC9C', rgb: '26, 188, 156', hsl: '168, 76%, 42%', anim: false, name: 'turquoise' }, 75 | { hex: '#17A589', rgb: '23, 165, 137', hsl: '168, 76%, 37%', anim: false, name: 'turquoise' }, 76 | { hex: '#148F77', rgb: '20, 143, 119', hsl: '168, 76%, 32%', anim: false, name: 'turquoise' }, 77 | { hex: '#117864', rgb: '17, 120, 100', hsl: '168, 76%, 27%', anim: false, name: 'turquoise' }, 78 | { hex: '#0E6251', rgb: '14, 98, 81', hsl: '168, 76%, 22%', anim: false, name: 'turquoise' }], 79 | 80 | [{ hex: '#E8F6F3', rgb: '232, 246, 243', hsl: '168, 42%, 94%', anim: false, name: 'green sea' }, 81 | { hex: '#D0ECE7', rgb: '208, 236, 231', hsl: '168, 42%, 87%', anim: false, name: 'green sea' }, 82 | { hex: '#A2D9CE', rgb: '162, 217, 206', hsl: '168, 42%, 74%', anim: false, name: 'green sea' }, 83 | { hex: '#73C6B6', rgb: '115, 198, 182', hsl: '168, 42%, 61%', anim: false, name: 'green sea' }, 84 | { hex: '#45B39D', rgb: '69, 179, 157', hsl: '168, 45%, 49%', anim: false, name: 'green sea' }, 85 | { hex: '#16A085', rgb: '22, 160, 133', hsl: '168, 76%, 36%', anim: false, name: 'green sea' }, 86 | { hex: '#138D75', rgb: '19, 141, 117', hsl: '168, 76%, 31%', anim: false, name: 'green sea' }, 87 | { hex: '#117A65', rgb: '17, 122, 101', hsl: '168, 76%, 27%', anim: false, name: 'green sea' }, 88 | { hex: '#0E6655', rgb: '14, 102, 85', hsl: '168, 76%, 23%', anim: false, name: 'green sea' }, 89 | { hex: '#0B5345', rgb: '11, 83, 69', hsl: '168, 76%, 19%', anim: false, name: 'green sea' }], 90 | 91 | [{ hex: '#E9F7EF', rgb: '233, 247, 239', hsl: '145, 45%, 94%', anim: false, name: 'nephritis' }, 92 | { hex: '#D4EFDF', rgb: '212, 239, 223', hsl: '145, 45%, 88%', anim: false, name: 'nephritis' }, 93 | { hex: '#A9DFBF', rgb: '169, 223, 191', hsl: '145, 45%, 77%', anim: false, name: 'nephritis' }, 94 | { hex: '#7DCEA0', rgb: '125, 206, 160', hsl: '145, 45%, 65%', anim: false, name: 'nephritis' }, 95 | { hex: '#52BE80', rgb: '82, 190, 128', hsl: '145, 45%, 53%', anim: false, name: 'nephritis' }, 96 | { hex: '#27AE60', rgb: '39, 174, 96', hsl: '145, 63%, 42%', anim: false, name: 'nephritis' }, 97 | { hex: '#229954', rgb: '34, 153, 84', hsl: '145, 63%, 37%', anim: false, name: 'nephritis' }, 98 | { hex: '#1E8449', rgb: '30, 132, 73', hsl: '145, 63%, 32%', anim: false, name: 'nephritis' }, 99 | { hex: '#196F3D', rgb: '25, 111, 61', hsl: '145, 63%, 27%', anim: false, name: 'nephritis' }, 100 | { hex: '#145A32', rgb: '20, 90, 50', hsl: '145, 63%, 22%', anim: false, name: 'nephritis' }], 101 | 102 | [{ hex: '#EAFAF1', rgb: '234, 250, 241', hsl: '145, 61%, 95%', anim: false, name: 'emerald' }, 103 | { hex: '#D5F5E3', rgb: '213, 245, 227', hsl: '145, 61%, 90%', anim: false, name: 'emerald' }, 104 | { hex: '#ABEBC6', rgb: '171, 235, 198', hsl: '145, 61%, 80%', anim: false, name: 'emerald' }, 105 | { hex: '#82E0AA', rgb: '130, 224, 170', hsl: '145, 61%, 69%', anim: false, name: 'emerald' }, 106 | { hex: '#58D68D', rgb: '88, 214, 141', hsl: '145, 61%, 59%', anim: false, name: 'emerald' }, 107 | { hex: '#2ECC71', rgb: '46, 204, 113', hsl: '145, 63%, 49%', anim: false, name: 'emerald' }, 108 | { hex: '#28B463', rgb: '40, 180, 99', hsl: '145, 63%, 43%', anim: false, name: 'emerald' }, 109 | { hex: '#239B56', rgb: '35, 155, 86', hsl: '145, 63%, 37%', anim: false, name: 'emerald' }, 110 | { hex: '#1D8348', rgb: '29, 131, 72', hsl: '145, 63%, 31%', anim: false, name: 'emerald' }, 111 | { hex: '#186A3B', rgb: '24, 106, 59', hsl: '145, 63%, 25%', anim: false, name: 'emerald' }], 112 | 113 | [{ hex: '#FEF9E7', rgb: '254, 249, 231', hsl: '48, 89%, 95%', anim: false, name: 'sunflower' }, 114 | { hex: '#FCF3CF', rgb: '252, 243, 207', hsl: '48, 89%, 90%', anim: false, name: 'sunflower' }, 115 | { hex: '#F9E79F', rgb: '249, 231, 159', hsl: '48, 89%, 80%', anim: false, name: 'sunflower' }, 116 | { hex: '#F7DC6F', rgb: '247, 220, 111', hsl: '48, 89%, 70%', anim: false, name: 'sunflower' }, 117 | { hex: '#F4D03F', rgb: '244, 208, 63', hsl: '48, 89%, 60%', anim: false, name: 'sunflower' }, 118 | { hex: '#F1C40F', rgb: '241, 196, 15', hsl: '48, 89%, 50%', anim: false, name: 'sunflower' }, 119 | { hex: '#D4AC0D', rgb: '212, 172, 13', hsl: '48, 88%, 44%', anim: false, name: 'sunflower' }, 120 | { hex: '#B7950B', rgb: '183, 149, 11', hsl: '48, 88%, 38%', anim: false, name: 'sunflower' }, 121 | { hex: '#9A7D0A', rgb: '154, 125, 10', hsl: '48, 88%, 32%', anim: false, name: 'sunflower' }, 122 | { hex: '#7D6608', rgb: '125, 102, 8', hsl: '48, 88%, 26%', anim: false, name: 'sunflower' }], 123 | 124 | [{ hex: '#FEF5E7', rgb: '254, 245, 231', hsl: '37, 90%, 95%', anim: false, name: 'orange' }, 125 | { hex: '#FDEBD0', rgb: '253, 235, 208', hsl: '37, 90%, 90%', anim: false, name: 'orange' }, 126 | { hex: '#FAD7A0', rgb: '250, 215, 160', hsl: '37, 90%, 80%', anim: false, name: 'orange' }, 127 | { hex: '#F8C471', rgb: '248, 196, 113', hsl: '37, 90%, 71%', anim: false, name: 'orange' }, 128 | { hex: '#F5B041', rgb: '245, 176, 65', hsl: '37, 90%, 61%', anim: false, name: 'orange' }, 129 | { hex: '#F39C12', rgb: '243, 156, 18', hsl: '37, 90%, 51%', anim: false, name: 'orange' }, 130 | { hex: '#D68910', rgb: '214, 137, 16', hsl: '37, 86%, 45%', anim: false, name: 'orange' }, 131 | { hex: '#B9770E', rgb: '185, 119, 14', hsl: '37, 86%, 39%', anim: false, name: 'orange' }, 132 | { hex: '#9C640C', rgb: '156, 100, 12', hsl: '37, 86%, 33%', anim: false, name: 'orange' }, 133 | { hex: '#7E5109', rgb: '126, 81, 9', hsl: '37, 86%, 27%', anim: false, name: 'orange' }], 134 | 135 | [{ hex: '#FDF2E9', rgb: '253, 242, 233', hsl: '28, 80%, 95%', anim: false, name: 'carrot' }, 136 | { hex: '#FAE5D3', rgb: '250, 229, 211', hsl: '28, 80%, 90%', anim: false, name: 'carrot' }, 137 | { hex: '#F5CBA7', rgb: '245, 203, 167', hsl: '28, 80%, 81%', anim: false, name: 'carrot' }, 138 | { hex: '#F0B27A', rgb: '240, 178, 122', hsl: '28, 80%, 71%', anim: false, name: 'carrot' }, 139 | { hex: '#EB984E', rgb: '235, 152, 78', hsl: '28, 80%, 61%', anim: false, name: 'carrot' }, 140 | { hex: '#E67E22', rgb: '230, 126, 34', hsl: '28, 80%, 52%', anim: false, name: 'carrot' }, 141 | { hex: '#CA6F1E', rgb: '202, 111, 30', hsl: '28, 74%, 46%', anim: false, name: 'carrot' }, 142 | { hex: '#AF601A', rgb: '175, 96, 26', hsl: '28, 74%, 39%', anim: false, name: 'carrot' }, 143 | { hex: '#935116', rgb: '147, 81, 22', hsl: '28, 74%, 33%', anim: false, name: 'carrot' }, 144 | { hex: '#784212', rgb: '120, 66, 18', hsl: '28, 74%, 27%', anim: false, name: 'carrot' }], 145 | 146 | [{ hex: '#FBEEE6', rgb: '251, 238, 230', hsl: '24, 71%, 94%', anim: false, name: 'pumpkin' }, 147 | { hex: '#F6DDCC', rgb: '246, 221, 204', hsl: '24, 71%, 88%', anim: false, name: 'pumpkin' }, 148 | { hex: '#EDBB99', rgb: '237, 187, 153', hsl: '24, 71%, 77%', anim: false, name: 'pumpkin' }, 149 | { hex: '#E59866', rgb: '229, 152, 102', hsl: '24, 71%, 65%', anim: false, name: 'pumpkin' }, 150 | { hex: '#DC7633', rgb: '220, 118, 51', hsl: '24, 71%, 53%', anim: false, name: 'pumpkin' }, 151 | { hex: '#D35400', rgb: '211, 84, 0', hsl: '24, 100%, 41%', anim: false, name: 'pumpkin' }, 152 | { hex: '#BA4A00', rgb: '186, 74, 0', hsl: '24, 100%, 36%', anim: false, name: 'pumpkin' }, 153 | { hex: '#A04000', rgb: '160, 64, 0', hsl: '24, 100%, 31%', anim: false, name: 'pumpkin' }, 154 | { hex: '#873600', rgb: '135, 54, 0', hsl: '24, 100%, 26%', anim: false, name: 'pumpkin' }, 155 | { hex: '#6E2C00', rgb: '110, 44, 0', hsl: '24, 100%, 22%', anim: false, name: 'pumpkin' }], 156 | 157 | [{ hex: '#FDFEFE', rgb: '253, 254, 254', hsl: '192, 15%, 99%', anim: false, name: 'clouds' }, 158 | { hex: '#FBFCFC', rgb: '251, 252, 252', hsl: '192, 15%, 99%', anim: false, name: 'clouds' }, 159 | { hex: '#F7F9F9', rgb: '247, 249, 249', hsl: '192, 15%, 97%', anim: false, name: 'clouds' }, 160 | { hex: '#F4F6F7', rgb: '244, 246, 247', hsl: '192, 15%, 96%', anim: false, name: 'clouds' }, 161 | { hex: '#F0F3F4', rgb: '240, 243, 244', hsl: '192, 15%, 95%', anim: false, name: 'clouds' }, 162 | { hex: '#ECF0F1', rgb: '236, 240, 241', hsl: '192, 15%, 94%', anim: false, name: 'clouds' }, 163 | { hex: '#D0D3D4', rgb: '208, 211, 212', hsl: '192, 5%, 82%', anim: false, name: 'clouds' }, 164 | { hex: '#B3B6B7', rgb: '179, 182, 183', hsl: '192, 3%, 71%', anim: false, name: 'clouds' }, 165 | { hex: '#979A9A', rgb: '151, 154, 154', hsl: '192, 2%, 60%', anim: false, name: 'clouds' }, 166 | { hex: '#7B7D7D', rgb: '123, 125, 125', hsl: '192, 1%, 49%', anim: false, name: 'clouds' }], 167 | 168 | [{ hex: '#F8F9F9', rgb: '248, 249, 249', hsl: '204, 8%, 98%', anim: false, name: 'silver' }, 169 | { hex: '#F2F3F4', rgb: '242, 243, 244', hsl: '204, 8%, 95%', anim: false, name: 'silver' }, 170 | { hex: '#E5E7E9', rgb: '229, 231, 233', hsl: '204, 8%, 90%', anim: false, name: 'silver' }, 171 | { hex: '#D7DBDD', rgb: '215, 219, 221', hsl: '204, 8%, 86%', anim: false, name: 'silver' }, 172 | { hex: '#CACFD2', rgb: '202, 207, 210', hsl: '204, 8%, 81%', anim: false, name: 'silver' }, 173 | { hex: '#BDC3C7', rgb: '189, 195, 199', hsl: '204, 8%, 76%', anim: false, name: 'silver' }, 174 | { hex: '#A6ACAF', rgb: '166, 172, 175', hsl: '204, 5%, 67%', anim: false, name: 'silver' }, 175 | { hex: '#909497', rgb: '144, 148, 151', hsl: '204, 4%, 58%', anim: false, name: 'silver' }, 176 | { hex: '#797D7F', rgb: '121, 125, 127', hsl: '204, 3%, 49%', anim: false, name: 'silver' }, 177 | { hex: '#626567', rgb: '98, 101, 103', hsl: '204, 3%, 40%', anim: false, name: 'silver' }], 178 | 179 | [{ hex: '#F4F6F6', rgb: '244, 246, 246', hsl: '184, 9%, 96%', anim: false, name: 'concrete' }, 180 | { hex: '#EAEDED', rgb: '234, 237, 237', hsl: '184, 9%, 92%', anim: false, name: 'concrete' }, 181 | { hex: '#D5DBDB', rgb: '213, 219, 219', hsl: '184, 9%, 85%', anim: false, name: 'concrete' }, 182 | { hex: '#BFC9CA', rgb: '191, 201, 202', hsl: '184, 9%, 77%', anim: false, name: 'concrete' }, 183 | { hex: '#AAB7B8', rgb: '170, 183, 184', hsl: '184, 9%, 69%', anim: false, name: 'concrete' }, 184 | { hex: '#95A5A6', rgb: '149, 165, 166', hsl: '184, 9%, 62%', anim: false, name: 'concrete' }, 185 | { hex: '#839192', rgb: '131, 145, 146', hsl: '184, 6%, 54%', anim: false, name: 'concrete' }, 186 | { hex: '#717D7E', rgb: '113, 125, 126', hsl: '184, 5%, 47%', anim: false, name: 'concrete' }, 187 | { hex: '#5F6A6A', rgb: '95, 106, 106', hsl: '184, 5%, 40%', anim: false, name: 'concrete' }, 188 | { hex: '#4D5656', rgb: '77, 86, 86', hsl: '184, 5%, 32%', anim: false, name: 'concrete' }], 189 | 190 | [{ hex: '#F2F4F4', rgb: '242, 244, 244', hsl: '184, 6%, 95%', anim: false, name: 'asbestos' }, 191 | { hex: '#E5E8E8', rgb: '229, 232, 232', hsl: '184, 6%, 91%', anim: false, name: 'asbestos' }, 192 | { hex: '#CCD1D1', rgb: '204, 209, 209', hsl: '184, 6%, 81%', anim: false, name: 'asbestos' }, 193 | { hex: '#B2BABB', rgb: '178, 186, 187', hsl: '184, 6%, 72%', anim: false, name: 'asbestos' }, 194 | { hex: '#99A3A4', rgb: '153, 163, 164', hsl: '184, 6%, 62%', anim: false, name: 'asbestos' }, 195 | { hex: '#7F8C8D', rgb: '127, 140, 141', hsl: '184, 6%, 53%', anim: false, name: 'asbestos' }, 196 | { hex: '#707B7C', rgb: '112, 123, 124', hsl: '184, 5%, 46%', anim: false, name: 'asbestos' }, 197 | { hex: '#616A6B', rgb: '97, 106, 107', hsl: '184, 5%, 40%', anim: false, name: 'asbestos' }, 198 | { hex: '#515A5A', rgb: '81, 90, 90', hsl: '184, 5%, 34%', anim: false, name: 'asbestos' }, 199 | { hex: '#424949', rgb: '66, 73, 73', hsl: '184, 5%, 27%', anim: false, name: 'asbestos' }], 200 | 201 | [{ hex: '#EBEDEF', rgb: '235, 237, 239', hsl: '210, 12%, 93%', anim: false, name: 'wet asphalt' }, 202 | { hex: '#D6DBDF', rgb: '214, 219, 223', hsl: '210, 12%, 86%', anim: false, name: 'wet asphalt' }, 203 | { hex: '#AEB6BF', rgb: '174, 182, 191', hsl: '210, 12%, 71%', anim: false, name: 'wet asphalt' }, 204 | { hex: '#85929E', rgb: '133, 146, 158', hsl: '210, 12%, 57%', anim: false, name: 'wet asphalt' }, 205 | { hex: '#5D6D7E', rgb: '93, 109, 126', hsl: '210, 15%, 43%', anim: false, name: 'wet asphalt' }, 206 | { hex: '#34495E', rgb: '52, 73, 94', hsl: '210, 29%, 29%', anim: false, name: 'wet asphalt' }, 207 | { hex: '#2E4053', rgb: '46, 64, 83', hsl: '210, 29%, 25%', anim: false, name: 'wet asphalt' }, 208 | { hex: '#283747', rgb: '40, 55, 71', hsl: '210, 29%, 22%', anim: false, name: 'wet asphalt' }, 209 | { hex: '#212F3C', rgb: '33, 47, 60', hsl: '210, 29%, 18%', anim: false, name: 'wet asphalt' }, 210 | { hex: '#1B2631', rgb: '27, 38, 49', hsl: '210, 29%, 15%', anim: false, name: 'wet asphalt' }], 211 | 212 | [{ hex: '#EAECEE', rgb: '234, 236, 238', hsl: '210, 9%, 92%', anim: false, name: 'midnight blue' }, 213 | { hex: '#D5D8DC', rgb: '213, 216, 220', hsl: '210, 9%, 85%', anim: false, name: 'midnight blue' }, 214 | { hex: '#ABB2B9', rgb: '171, 178, 185', hsl: '210, 9%, 70%', anim: false, name: 'midnight blue' }, 215 | { hex: '#808B96', rgb: '128, 139, 150', hsl: '210, 9%, 55%', anim: false, name: 'midnight blue' }, 216 | { hex: '#566573', rgb: '86, 101, 115', hsl: '210, 14%, 39%', anim: false, name: 'midnight blue' }, 217 | { hex: '#2C3E50', rgb: '44, 62, 80', hsl: '210, 29%, 24%', anim: false, name: 'midnight blue' }, 218 | { hex: '#273746', rgb: '39, 55, 70', hsl: '210, 29%, 21%', anim: false, name: 'midnight blue' }, 219 | { hex: '#212F3D', rgb: '33, 47, 61', hsl: '210, 29%, 18%', anim: false, name: 'midnight blue' }, 220 | { hex: '#1C2833', rgb: '28, 40, 51', hsl: '210, 29%, 16%', anim: false, name: 'midnight blue' }, 221 | { hex: '#17202A', rgb: '23, 32, 42', hsl: '210, 29%, 13%', anim: false, name: 'midnight blue' }] 222 | ], 223 | currentColor: { hex: '', rgb: '', hsl: '', name: '', index: 0 }, 224 | toggleColorForm: false, 225 | colorHex: 'ffffff', 226 | colorRgb: '255,255,255', 227 | colorHsl: '0,0%,100%' 228 | } 229 | 230 | const mutations = { 231 | SET_CURRENT_COLOR (state, obj) { 232 | state.currentColor = obj 233 | }, 234 | ACTIVE_COLOR (state, payload) { 235 | state.colorMatrix[payload.row][payload.col]['anim'] = true 236 | }, 237 | DISACTIVE_COLOR (state, payload) { 238 | state.colorMatrix[payload.row][payload.col]['anim'] = false 239 | }, 240 | TOGGLE_FORM (state) { 241 | state.toggleColorForm = !state.toggleColorForm 242 | }, 243 | SET_HEX_COLOR (state, value) { 244 | function hexToRgb (hex) { 245 | // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") 246 | let shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i 247 | hex = hex.replace(shorthandRegex, function (m, r, g, b) { 248 | return r + r + g + g + b + b 249 | }) 250 | 251 | let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex) 252 | return result ? [ 253 | parseInt(result[1], 16), 254 | parseInt(result[2], 16), 255 | parseInt(result[3], 16) 256 | ] : null 257 | } 258 | 259 | function rgb2hsl (r, g, b) { 260 | let a = Math.max(r, g, b) 261 | let n = a - Math.min(r, g, b) 262 | let f = (1 - Math.abs(a + a - n - 1)) 263 | let h = n && ((a === r) ? (g - b) / n : ((a === g) ? 2 + (b - r) / n : 4 + (r - g) / n)) 264 | return [60 * (h < 0 ? h + 6 : h), f ? n / f : 0, (a + a - n) / 2] 265 | } 266 | 267 | try { 268 | state.colorHex = value 269 | state.colorRgb = hexToRgb(state.colorHex).join(',') 270 | let rgbArrayBase = state.colorRgb.split(',').map((rgb) => rgb / 255) 271 | state.colorHsl = rgb2hsl(...rgbArrayBase).map((hsl, index) => { 272 | return index ? (hsl * 100).toFixed(0) + '%' : hsl.toFixed(0) 273 | }).join(',') 274 | } catch (err) { 275 | console.log(err.message) 276 | } 277 | }, 278 | 279 | SET_RGB_COLOR (state, payload) { 280 | function rgb2hsl (r, g, b) { 281 | let a = Math.max(r, g, b) 282 | let n = a - Math.min(r, g, b) 283 | let f = (1 - Math.abs(a + a - n - 1)) 284 | let h = n && ((a === r) ? (g - b) / n : ((a === g) ? 2 + (b - r) / n : 4 + (r - g) / n)) 285 | return [60 * (h < 0 ? h + 6 : h), f ? n / f : 0, (a + a - n) / 2] 286 | } 287 | let rgb2hex = (r, g, b) => [r, g, b].map(x => Math.round(x * 255).toString(16).padStart(2, 0)).join('') 288 | 289 | try { 290 | let rgbArray = state.colorRgb.split(',') 291 | if (payload.sym === 'r') { 292 | rgbArray[0] = payload.value 293 | } else if (payload.sym === 'g') { 294 | rgbArray[1] = payload.value 295 | } else if (payload.sym === 'b') { 296 | rgbArray[2] = payload.value 297 | } else { 298 | rgbArray = [255, 255, 255] 299 | } 300 | let rgbArrayBase = rgbArray.map((rgb) => rgb / 255) 301 | state.colorRgb = rgbArray.join(',') 302 | state.colorHex = rgb2hex(...rgbArrayBase) 303 | state.colorHsl = rgb2hsl(...rgbArrayBase).map((hsl, index) => { 304 | return index ? (hsl * 100).toFixed(0) + '%' : hsl.toFixed(0) 305 | }).join(',') 306 | } catch (err) { 307 | console.log(err.message) 308 | } 309 | }, 310 | 311 | SET_HSL_COLOR (state, payload) { 312 | let hsl2rgb = (h, s, l, a = s * Math.min(l, 1 - l), f = (n, k = (n + h / 30) % 12) => l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1)) => [f(0), f(8), f(4)] 313 | let rgb2hex = (r, g, b) => [r, g, b].map(x => Math.round(x * 255).toString(16).padStart(2, 0)).join('') 314 | try { 315 | let hslArray = state.colorHsl.split(',') 316 | let hslArrayBase = hslArray.map((hsl, i) => { 317 | return i ? (hsl.split('%')[0] / 100) : hsl 318 | }) 319 | if (payload.sym === 'h') { 320 | hslArray[0] = payload.value 321 | hslArrayBase[0] = payload.value 322 | } else if (payload.sym === 's') { 323 | hslArray[1] = payload.value + '%' 324 | hslArrayBase[1] = payload.value / 100 325 | } else if (payload.sym === 'l') { 326 | hslArray[2] = payload.value + '%' 327 | hslArrayBase[2] = payload.value / 100 328 | } else { 329 | hslArray = ['0', '0%', '100%'] 330 | } 331 | state.colorHsl = hslArray.join(',') 332 | state.colorRgb = hsl2rgb(...hslArrayBase).map(x => x * 255 | 0).join(',') 333 | let rgbArrayBase = state.colorRgb.split(',').map((rgb) => rgb / 255) 334 | state.colorHex = rgb2hex(...rgbArrayBase) 335 | } catch (err) { 336 | console.log(err.message) 337 | } 338 | } 339 | } 340 | 341 | const actions = { 342 | setCurrentColor ({ commit }, obj) { 343 | commit('SET_CURRENT_COLOR', obj) 344 | }, 345 | activeColor ({ commit }, payload) { 346 | commit('ACTIVE_COLOR', payload) 347 | }, 348 | disactiveColor ({ commit }, payload) { 349 | commit('DISACTIVE_COLOR', payload) 350 | }, 351 | toggleForm ({ commit }) { 352 | commit('TOGGLE_FORM') 353 | }, 354 | setHexColor ({ commit }, payload) { 355 | commit('SET_HEX_COLOR', payload.target.value) 356 | }, 357 | setRgbColor ({ commit }, payload) { 358 | commit('SET_RGB_COLOR', payload) 359 | }, 360 | setHslColor ({ commit }, payload) { 361 | commit('SET_HSL_COLOR', payload) 362 | } 363 | } 364 | 365 | export default { 366 | state, 367 | mutations, 368 | actions 369 | } 370 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Cron.js: -------------------------------------------------------------------------------- 1 | import i18n from './../../i18n' 2 | import * as moment from 'moment' 3 | import * as parser from 'cron-parser' 4 | 5 | const CRON_DATETIME_MAX_LIMIT = 200 6 | const CRON_DATETIME_DEFAULT_COUNT = 7 7 | const CRON_SPECIAL_TYPES = [ '@yearly', '@annually', '@monthly', '@daily', '@hourly', '@reboot' ] 8 | const CRON_SELECTED_UNIT = { 9 | 'min': false, 10 | 'hour': false, 11 | 'dayOfMonth': false, 12 | 'month': false, 13 | 'dayOfWeek': false 14 | } 15 | const CRON_OPTIONS = { 16 | currentDate: moment.utc().local().format('YYYY-MM-DD HH:mm:ss'), 17 | utc: true 18 | } 19 | 20 | const helpers = { 21 | getTermOptions () { 22 | return { 23 | default: [ 24 | { symbol: '*', explaination: i18n.t('cronHelp.anyValue') }, 25 | { symbol: ',', explaination: i18n.t('cronHelp.valueListSeparator') }, 26 | { symbol: '-', explaination: i18n.t('cronHelp.rangeOfValues') }, 27 | { symbol: '/', explaination: i18n.t('cronHelp.stepValues') }, 28 | { symbol: '@yearly', explaination: i18n.t('cronHelp.nonStandard') }, 29 | { symbol: '@annually', explaination: i18n.t('cronHelp.nonStandard') }, 30 | { symbol: '@monthly', explaination: i18n.t('cronHelp.nonStandard') }, 31 | { symbol: '@daily', explaination: i18n.t('cronHelp.nonStandard') }, 32 | { symbol: '@hourly', explaination: i18n.t('cronHelp.nonStandard') }, 33 | { symbol: '@reboot', explaination: i18n.t('cronHelp.nonStandard') } 34 | ], 35 | min: [ 36 | { symbol: '*', explaination: i18n.t('cronHelp.anyValue') }, 37 | { symbol: ',', explaination: i18n.t('cronHelp.valueListSeparator') }, 38 | { symbol: '-', explaination: i18n.t('cronHelp.rangeOfValues') }, 39 | { symbol: '/', explaination: i18n.t('cronHelp.stepValues') }, 40 | { symbol: '0-59', explaination: i18n.t('cronHelp.allowedValues') } 41 | ], 42 | hour: [ 43 | { symbol: '*', explaination: i18n.t('cronHelp.anyValue') }, 44 | { symbol: ',', explaination: i18n.t('cronHelp.valueListSeparator') }, 45 | { symbol: '-', explaination: i18n.t('cronHelp.rangeOfValues') }, 46 | { symbol: '/', explaination: i18n.t('cronHelp.stepValues') }, 47 | { symbol: '0-23', explaination: i18n.t('cronHelp.allowedValues') } 48 | ], 49 | dayOfMonth: [ 50 | { symbol: '*', explaination: i18n.t('cronHelp.anyValue') }, 51 | { symbol: ',', explaination: i18n.t('cronHelp.valueListSeparator') }, 52 | { symbol: '-', explaination: i18n.t('cronHelp.rangeOfValues') }, 53 | { symbol: '/', explaination: i18n.t('cronHelp.stepValues') }, 54 | { symbol: '1-31', explaination: i18n.t('cronHelp.allowedValues') } 55 | ], 56 | month: [ 57 | { symbol: '*', explaination: i18n.t('cronHelp.anyValue') }, 58 | { symbol: ',', explaination: i18n.t('cronHelp.valueListSeparator') }, 59 | { symbol: '-', explaination: i18n.t('cronHelp.rangeOfValues') }, 60 | { symbol: '/', explaination: i18n.t('cronHelp.stepValues') }, 61 | { symbol: '1-12', explaination: i18n.t('cronHelp.allowedValues') }, 62 | { symbol: 'JAN-DEC', explaination: i18n.t('cronHelp.alternativeSingleValues') } 63 | ], 64 | dayOfWeek: [ 65 | { symbol: '*', explaination: i18n.t('cronHelp.anyValue') }, 66 | { symbol: ',', explaination: i18n.t('cronHelp.valueListSeparator') }, 67 | { symbol: '-', explaination: i18n.t('cronHelp.rangeOfValues') }, 68 | { symbol: '/', explaination: i18n.t('cronHelp.stepValues') }, 69 | { symbol: '0-6', explaination: i18n.t('cronHelp.allowedValues') }, 70 | { symbol: 'SUN-SAT', explaination: i18n.t('cronHelp.alternativeSingleValues') } 71 | ] 72 | } 73 | }, 74 | 75 | translateSpecialWord () { 76 | if (state.cronExp === '@yearly') { 77 | state.cronResult['content'] = i18n.t('cronHelp.yearly') 78 | return true 79 | } 80 | if (state.cronExp === '@annually') { 81 | state.cronResult['content'] = i18n.t('cronHelp.annually') 82 | return true 83 | } 84 | if (state.cronExp === '@monthly') { 85 | state.cronResult['content'] = i18n.t('cronHelp.monthly') 86 | return true 87 | } 88 | if (state.cronExp === '@daily') { 89 | state.cronResult['content'] = i18n.t('cronHelp.daily') 90 | return true 91 | } 92 | if (state.cronExp === '@hourly') { 93 | state.cronResult['content'] = i18n.t('cronHelp.hourly') 94 | return true 95 | } 96 | if (state.cronExp === '@reboot') { 97 | state.cronResult['content'] = i18n.t('cronHelp.afterReboot') 98 | return true 99 | } 100 | return false 101 | }, 102 | 103 | translateToLanguage (state) { 104 | if (this.translateSpecialWord()) return true 105 | 106 | let phrases = state.cronExp.split(/\s+/) 107 | switch (state.currentUnit) { 108 | case 'min': 109 | state.cronResult['content'] = i18n.t('cron.at') + ' ' + this.translateLanguageByMode(phrases[0], 'minute') 110 | break 111 | case 'hour': 112 | state.cronResult['content'] = i18n.t('cron.at') + ' ' + this.translateLanguageByMode(phrases[1], 'hour') 113 | break 114 | case 'dayOfMonth': 115 | state.cronResult['content'] = i18n.t('cron.on') + ' ' + this.translateLanguageByMode(phrases[2], 'dayOfMonth') 116 | break 117 | case 'month': 118 | state.cronResult['content'] = i18n.t('cron.in') + ' ' + this.translateLanguageByMode(phrases[3], 'month') 119 | break 120 | case 'dayOfWeek': 121 | state.cronResult['content'] = i18n.t('cron.on') + ' ' + this.translateLanguageByMode(phrases[4], 'dayOfWeek') 122 | break 123 | default: 124 | state.cronResult['content'] = '' 125 | break 126 | } 127 | if (i18n.locale === 'cn') state.cronResult['content'] = state.cronResult['content'].replace(/\s/g, '') 128 | }, 129 | 130 | translateLanguageByMode (phrase, mode) { 131 | let terms = phrase.split(',') 132 | if (terms.length === 1) return this.translateEachTerm(terms[0], mode) 133 | if (terms.length > 1) { 134 | let lang = '' 135 | if (i18n.locale === 'en' && mode === 'dayOfMonth') lang += i18n.t('cronHelp.dayOfMonth') 136 | for (let i = 0; i < terms.length; i++) { 137 | if (i === (terms.length - 2)) { 138 | lang = lang + this.translateEachTerm(terms[i], mode) + ' ' + i18n.t('cron.and') + ' ' 139 | } else if (i === (terms.length - 1)) { 140 | lang = lang + ' ' + this.translateEachTerm(terms[i], mode) + ' ' 141 | } else { 142 | lang = lang + this.translateEachTerm(terms[i], mode) + ', ' 143 | } 144 | } 145 | return lang 146 | } else { 147 | return '' 148 | } 149 | }, 150 | 151 | translateEachTerm (term, mode, fromRange = false) { 152 | if (term === '*') { 153 | return this.translateEveryTerm(mode) 154 | } else if (term.match(/\//)) { 155 | let skip = term.split(/\//) 156 | let _range = this.translateRange(skip[0], mode, true) 157 | if (i18n.locale === 'cn') { 158 | return i18n.t('cron.every') + _range + i18n.t('cron.of') + i18n.t('cron.every') + this.translateOrdinalSuffix(skip[1], mode) 159 | } else { 160 | return i18n.t('cron.every') + ' ' + this.translateOrdinalSuffix(skip[1], mode) + ' ' + _range 161 | } 162 | } else if (term.match(/-/)) { 163 | return this.translateRange(term, mode) 164 | } else { 165 | if (mode === 'dayOfWeek') { 166 | return this.getDayOfWeek(term) 167 | } else if (mode === 'month') { 168 | return this.getMonthName(term) 169 | } else { 170 | return (i18n.locale === 'en' && (fromRange || mode === 'dayOfMonth')) ? term : (term + ' ' + i18n.t('cronHelp.' + mode)) 171 | } 172 | } 173 | }, 174 | 175 | translateEveryTerm (mode) { 176 | if (mode === 'dayOfWeek' && i18n.locale === 'cn') return '每周的每一天' 177 | if (mode === 'dayOfMonth' && i18n.locale === 'cn') return '每月的每一天' 178 | return i18n.t('cron.every') + ' ' + i18n.t('cronHelp.' + mode) 179 | }, 180 | 181 | translateRange (term, mode, fromStep = false) { 182 | if (term === '*') { 183 | return '' 184 | } else if (term.match('-')) { 185 | let range = term.split(/-/) 186 | let rangeLang = i18n.t('cron.from') + ' ' + this.translateEachTerm(range[0], mode, true) + ' ' + i18n.t('cron.through') + ' ' + this.translateEachTerm(range[1], mode, true) 187 | return (i18n.locale === 'en' && !fromStep) ? (i18n.t('cronHelp.' + mode) + ' ' + rangeLang) : rangeLang 188 | } else if (fromStep === true) { 189 | let rangeLang = i18n.t('cron.from') + ' ' + this.translateEachTerm(term, mode, true) + ' ' + i18n.t('cron.through') + ' ' + this.translateEachTerm(this.getTheEndByMode(mode), mode, true) 190 | return (i18n.locale === 'en' && !fromStep) ? (i18n.t('cronHelp.' + mode) + ' ' + rangeLang) : rangeLang 191 | } 192 | }, 193 | 194 | translateOrdinalSuffix (num, mode) { 195 | if (i18n.locale === 'en') { 196 | if (parseInt(num) === 1) return '1st' + ' ' + i18n.t('cronHelp.' + mode) 197 | if (parseInt(num) === 2) return '2nd' + ' ' + i18n.t('cronHelp.' + mode) 198 | if (parseInt(num) === 3) return '3rd' + ' ' + i18n.t('cronHelp.' + mode) 199 | if (parseInt(num) > 3) return num + 'th' + ' ' + i18n.t('cronHelp.' + mode) 200 | } else { 201 | let lang = '第' + num 202 | return (mode === 'dayOfWeek' || mode === 'dayOfMonth') ? lang + '天' : lang + i18n.t('cronHelp.' + mode) 203 | } 204 | }, 205 | 206 | getDayOfWeek (num) { 207 | let dayOfWeek = [ 208 | i18n.t('cronHelp.sunday'), 209 | i18n.t('cronHelp.monday'), 210 | i18n.t('cronHelp.tuesday'), 211 | i18n.t('cronHelp.wednesday'), 212 | i18n.t('cronHelp.thursday'), 213 | i18n.t('cronHelp.friday'), 214 | i18n.t('cronHelp.saturday') 215 | ] 216 | let aliases = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'] 217 | let numStr = String(num).toLowerCase() 218 | if (aliases.includes(numStr)) { 219 | num = aliases.findIndex(x => x === numStr) 220 | } 221 | return dayOfWeek[num] 222 | }, 223 | 224 | getDayOfMonth (num) { 225 | return (state.locale === 'en') ? num : (num + i18n.t('cronHelp.dayOfMonth')) 226 | }, 227 | 228 | getMonthName (num) { 229 | let dayOfMonth = { 230 | 1: 'January', 231 | 2: 'February', 232 | 3: 'March', 233 | 4: 'April', 234 | 5: 'May', 235 | 6: 'June', 236 | 7: 'July', 237 | 8: 'August', 238 | 9: 'September', 239 | 10: 'October', 240 | 11: 'November', 241 | 12: 'December' 242 | } 243 | 244 | let aliases = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'] 245 | let numStr = String(num).toLowerCase() 246 | if (aliases.includes(numStr)) { 247 | num = aliases.findIndex(x => x === numStr) + 1 248 | } 249 | 250 | return (i18n.locale === 'en') ? dayOfMonth[num] : (num + i18n.t('cronHelp.month')) 251 | }, 252 | 253 | getTheEndByMode (mode) { 254 | if (mode === 'minute') return '59' 255 | if (mode === 'hour') return '23' 256 | if (mode === 'dayOfMonth') return '31' 257 | if (mode === 'month') return '12' 258 | if (mode === 'dayOfWeek') return '0' 259 | } 260 | 261 | } 262 | 263 | const state = { 264 | tabs: [ 265 | { title: i18n.t('tabs.home'), isActive: true }, 266 | { title: i18n.t('tabs.help'), isActive: false } 267 | ], 268 | currentTab: i18n.t('tabs.home'), 269 | cronExp: '* * * * *', 270 | dateMaxCount: CRON_DATETIME_DEFAULT_COUNT, 271 | cronResult: { 272 | status: 0, 273 | content: i18n.t('cron.heresResult'), 274 | list: [] 275 | }, 276 | termOptions: helpers.getTermOptions(), 277 | currentUnit: 'default' 278 | } 279 | 280 | const mutations = { 281 | SET_INIT_STATE (state, payload) { 282 | state.tabs = payload.tabs 283 | state.currentTab = payload.currentTab 284 | state.cronResult = { status: 0, content: i18n.t('cron.heresResult'), list: [] } 285 | state.termOptions = helpers.getTermOptions() 286 | }, 287 | 288 | SET_NAV (state, payload) { 289 | state.tabs.forEach((tabs, i) => { 290 | if (i === payload.index) { 291 | state.tabs[payload.index]['isActive'] = true 292 | state.currentTab = payload.title 293 | } else { 294 | state.tabs[i]['isActive'] = false 295 | } 296 | }) 297 | }, 298 | 299 | CRON_MATCH (state, target) { 300 | if (target.name === 'cron-exp') { 301 | state.cronExp = target.value 302 | } 303 | if (target.name === 'max-count') { 304 | if (target.value >= 1 && target.value <= CRON_DATETIME_MAX_LIMIT) { 305 | state.dateMaxCount = parseInt(target.value) 306 | } else if (target.value > CRON_DATETIME_MAX_LIMIT) { 307 | state.dateMaxCount = CRON_DATETIME_MAX_LIMIT 308 | } else if (target.value === '') { 309 | state.dateMaxCount = '' 310 | return false 311 | } else { 312 | state.dateMaxCount = CRON_DATETIME_DEFAULT_COUNT 313 | } 314 | } 315 | 316 | try { 317 | let expTerms = state.cronExp.split(/\s+/) 318 | let terms = expTerms.filter((term) => term.length !== 0) 319 | state.cronResult = { status: -1, content: '', list: [] } 320 | if (terms.length < 5 && !CRON_SPECIAL_TYPES.includes(state.cronExp)) { 321 | target.classList.add('invalid') 322 | state.cronResult = { status: -1, content: i18n.t('cronHelp.expNotComplete'), list: [] } 323 | return false 324 | } else { 325 | target.classList.remove('invalid') 326 | if (state.cronExp === '@reboot') { 327 | state.cronResult = { status: 1, content: i18n.t('cronHelp.afterReboot'), list: [] } 328 | return false 329 | } 330 | } 331 | let interval = (state.cronExp === '@annually') ? parser.parseExpression('0 0 1 1 *', CRON_OPTIONS) : parser.parseExpression(state.cronExp, CRON_OPTIONS) 332 | Array(parseInt(state.dateMaxCount)).fill(1).forEach((_, i) => { 333 | state.cronResult['list'].push(interval.next()['_date'].format('YYYY-MM-DD HH:mm:ss dddd')) 334 | }) 335 | state.cronResult['status'] = 1 336 | if (state.cronResult['status'] >= 0) helpers.translateToLanguage(state) 337 | } catch (err) { 338 | console.log('Error: ' + err.message) 339 | target.classList.add('invalid') 340 | state.cronResult = { status: -1, content: err.message, list: [] } 341 | return false 342 | } 343 | }, 344 | 345 | SELECT_UNIT (state, target) { 346 | let children = target.currentTarget.parentNode.childNodes 347 | children.forEach((child, i) => { 348 | if (child.classList && child.classList.contains('tag')) { 349 | child.classList.remove('selected') 350 | } 351 | }) 352 | target.currentTarget.classList.add('selected') 353 | selectTextInInput() 354 | if (state.cronResult['status'] >= 0) helpers.translateToLanguage(state) 355 | 356 | function selectTextInInput () { 357 | let expTerms = state.cronExp.split(' ') 358 | let terms = expTerms.filter((term) => term.length !== 0) 359 | if (terms.length === 5) { 360 | let cronInput = document.getElementsByName('cron-exp')[0] 361 | cronInput.focus() 362 | if (target.currentTarget.classList.contains('min')) { 363 | cronInput.setSelectionRange(0, terms[0].length) 364 | state.currentUnit = 'min' 365 | } 366 | if (target.currentTarget.classList.contains('hour')) { 367 | cronInput.setSelectionRange((terms[0].length + 1), (terms[1].length + terms[0].length + 1)) 368 | state.currentUnit = 'hour' 369 | } 370 | if (target.currentTarget.classList.contains('dayOfMonth')) { 371 | let endTermLen = getStrLengthInArray(terms, 0, 3) 372 | let endPos = endTermLen + 2 373 | let startTermLen = getStrLengthInArray(terms, 0, 2) 374 | let startPos = startTermLen + 2 375 | cronInput.setSelectionRange(startPos, endPos) 376 | state.currentUnit = 'dayOfMonth' 377 | } 378 | if (target.currentTarget.classList.contains('month')) { 379 | let endTermLen = getStrLengthInArray(terms, 0, 4) 380 | let endPos = endTermLen + 3 381 | let startTermLen = getStrLengthInArray(terms, 0, 3) 382 | let startPos = startTermLen + 3 383 | cronInput.setSelectionRange(startPos, endPos) 384 | state.currentUnit = 'month' 385 | } 386 | if (target.currentTarget.classList.contains('dayOfWeek')) { 387 | let endTermLen = getStrLengthInArray(terms, 0, 5) 388 | let endPos = endTermLen + 4 389 | let startTermLen = getStrLengthInArray(terms, 0, 4) 390 | let startPos = startTermLen + 4 391 | cronInput.setSelectionRange(startPos, endPos) 392 | state.currentUnit = 'dayOfWeek' 393 | } 394 | } 395 | 396 | function getStrLengthInArray (array, start, end) { 397 | return array.slice(start, end).map((elem, i) => { return elem.length }).reduce((sum, x) => sum + x) 398 | } 399 | } 400 | }, 401 | 402 | UNIT_BE_SELECTED (state, target) { 403 | let ctl = target.currentTarget 404 | let startPos = ctl.selectionStart 405 | let endPos = ctl.selectionEnd 406 | 407 | let expTerms = state.cronExp.split(' ') 408 | let terms = expTerms.filter((term) => term.length !== 0) 409 | if (terms.length === 5) { 410 | let ranges = getStrRange(terms) 411 | let unit = Object.keys(CRON_SELECTED_UNIT) 412 | let tagList = Array.from(document.getElementsByClassName('tag')) 413 | for (let tag of tagList) { 414 | tag.classList.remove('selected') 415 | } 416 | ranges.forEach((range, i) => { 417 | if (startPos >= range[0] && startPos <= range[1] && endPos >= range[0] && endPos <= range[1]) { 418 | document.getElementsByClassName(unit[i])[0].classList.add('selected') 419 | state.currentUnit = unit[i] 420 | if (state.cronResult['status'] >= 0) helpers.translateToLanguage(state) 421 | } 422 | }) 423 | } 424 | 425 | if (CRON_SPECIAL_TYPES.includes(state.cronExp)) { 426 | state.currentUnit = 'default' 427 | } 428 | 429 | function getStrRange (array) { 430 | let pos = 0 431 | let ranges = array.map(function (elem, i) { 432 | let range = [pos, pos + elem.length] 433 | pos = pos + elem.length 434 | pos += 1 // add space 435 | return range 436 | }) 437 | return ranges 438 | } 439 | } 440 | } 441 | 442 | const actions = { 443 | setInitState ({ commit }, payload) { 444 | commit('SET_INIT_STATE', payload) 445 | }, 446 | setNav ({ commit }, payload) { 447 | commit('SET_NAV', payload) 448 | }, 449 | cronMatch ({ commit }, payload) { 450 | commit('CRON_MATCH', payload.target) 451 | }, 452 | selectUnit ({ commit }, payload) { 453 | commit('SELECT_UNIT', payload) 454 | }, 455 | unitBeSelected ({ commit }, payload) { 456 | commit('UNIT_BE_SELECTED', payload) 457 | } 458 | } 459 | 460 | export default { 461 | state, 462 | mutations, 463 | actions 464 | } 465 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Image64.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | base64: '' 3 | } 4 | 5 | const mutations = { 6 | SET_IMAGE (state, payload) { 7 | let image = payload 8 | state.base64 = image 9 | }, 10 | RESET_ALL () { 11 | state.base64 = '' 12 | } 13 | } 14 | 15 | const actions = { 16 | setImage ({ commit }, payload) { 17 | commit('SET_IMAGE', payload) 18 | }, 19 | resetAll ({ commit }) { 20 | commit('RESET_ALL') 21 | } 22 | } 23 | 24 | export default { 25 | state, 26 | mutations, 27 | actions 28 | } 29 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Json.js: -------------------------------------------------------------------------------- 1 | import i18n from './../../i18n' 2 | 3 | const state = { 4 | jsonStr: '', 5 | jsonResult: '', 6 | jsonHtml: '', 7 | error: { 'code': 0, 'msg': '' }, 8 | isXML: false 9 | } 10 | 11 | const ESCAPE_SYM = [ 12 | ['<', '<'], 13 | ['>', '>'], 14 | [' ', '&nbsp'], 15 | ['"', '&quot'] 16 | ] 17 | 18 | const mutations = { 19 | SET_JSON_TEXT (state, value) { 20 | state.jsonStr = value 21 | }, 22 | 23 | CONVERT_JSON (state) { 24 | let i = 0 25 | let indentInit = 0 26 | let indentSym = ' ' 27 | let indentCount = 4 28 | 29 | state.error = { 'code': 0, 'msg': '' } 30 | state.jsonResult = parseValue() 31 | skipSpace() 32 | if (i < state.jsonStr.length) { 33 | state.jsonResult = errorTermination(i18n.t('json.moreBehind')) 34 | } 35 | state.jsonHtml = renderHtml(state.jsonResult, indentInit) 36 | state.isXML = false 37 | 38 | function escapeString (txt) { 39 | ESCAPE_SYM.forEach((symItem) => { 40 | let rex = new RegExp(symItem[0], 'g') 41 | txt = txt.replace(rex, symItem[1]) 42 | }) 43 | return txt 44 | } 45 | 46 | function unicodeToChinese (str) { 47 | return unescape(str.replace(/\\u/g, '%u')) 48 | } 49 | 50 | function renderHtml (result, indtIndx) { 51 | let rsltHtml = '' 52 | let indentI = indtIndx + 1 53 | if (state.error['code'] === -1) { 54 | rsltHtml = '
' + i18n.t('json.error') + ':
' + state.error['msg'] + '
' + i18n.t('json.near') + '

' + state.error['posMsg'] + '

' 55 | } else if (Array.isArray(result)) { 56 | let arrayHtml = '' 57 | let arrayItemCount = 0 58 | result.forEach((item, index) => { 59 | arrayItemCount++ 60 | let itemHtml = '' 61 | if (typeof item === 'number') { 62 | if (index < (result.length - 1)) { 63 | itemHtml = indentSym.repeat(indentCount * indentI) + '' + item + ',
' 64 | } else { 65 | itemHtml = indentSym.repeat(indentCount * indentI) + '' + item + '
' 66 | } 67 | } else if (typeof item === 'string') { 68 | if (index < (result.length - 1)) { 69 | itemHtml = indentSym.repeat(indentCount * indentI) + '"' + unicodeToChinese(item) + '",
' 70 | } else { 71 | itemHtml = indentSym.repeat(indentCount * indentI) + '"' + unicodeToChinese(item) + '"
' 72 | } 73 | } else if (typeof item === 'boolean') { 74 | if (index < (result.length - 1)) { 75 | itemHtml = indentSym.repeat(indentCount * indentI) + '' + item + ',
' 76 | } else { 77 | itemHtml = indentSym.repeat(indentCount * indentI) + '' + item + '
' 78 | } 79 | } else if (item === null) { 80 | if (index < (result.length - 1)) { 81 | itemHtml = indentSym.repeat(indentCount * indentI) + '' + item + ',
' 82 | } else { 83 | itemHtml = indentSym.repeat(indentCount * indentI) + '' + item + '
' 84 | } 85 | } else { 86 | if (index < (result.length - 1)) { 87 | itemHtml = indentSym.repeat(indentCount * indentI) + renderHtml(item, indentI) + ',
' 88 | } else { 89 | itemHtml = indentSym.repeat(indentCount * indentI) + renderHtml(item, indentI) + '
' 90 | } 91 | } 92 | arrayHtml += itemHtml 93 | }) 94 | rsltHtml = '[
' + arrayHtml + indentSym.repeat(indentCount * indtIndx) + ']
' 95 | } else if (typeof result === 'object') { 96 | let resultKeyVal = '' 97 | for (let index = 0; index < Object.entries(result).length; index++) { 98 | // deal with key 99 | let keyval = Object.entries(result)[index] 100 | let resultKey 101 | // if (typeof keyval[0] === 'number' || (keyval[0] === '0')) { 102 | if (typeof keyval[0] === 'number') { 103 | return 'Error!' 104 | } else { 105 | resultKey = indentSym.repeat(indentCount * indentI) + '"' + unicodeToChinese(escapeString(keyval[0])) + '":' 106 | } 107 | 108 | // deal with value 109 | let resultVal 110 | if (typeof keyval[1] === 'number') { 111 | if (index < (Object.entries(result).length - 1)) { 112 | resultVal = '' + keyval[1] + ',
' 113 | } else { 114 | resultVal = '' + keyval[1] + '
' 115 | } 116 | } else if (typeof keyval[1] === 'string') { 117 | if (index < (Object.entries(result).length - 1)) { 118 | resultVal = '"' + unicodeToChinese(escapeString(keyval[1])) + '",
' 119 | } else { 120 | resultVal = '"' + unicodeToChinese(escapeString(keyval[1])) + '"
' 121 | } 122 | } else if (keyval[1] === null) { 123 | if (index < (Object.entries(result).length - 1)) { 124 | resultVal = 'null,
' 125 | } else { 126 | resultVal = 'null
' 127 | } 128 | } else { 129 | if (index < (Object.entries(result).length - 1)) { 130 | resultVal = renderHtml(keyval[1], indentI) + ',
' 131 | } else { 132 | resultVal = renderHtml(keyval[1], indentI) + '
' 133 | } 134 | } 135 | 136 | // adding ',' if it's not the last key-val 137 | resultKeyVal += (resultKey + resultVal) 138 | } 139 | rsltHtml = '{
' + resultKeyVal + indentSym.repeat(indentCount * indtIndx) + '}
' 140 | } else if (typeof result === 'boolean') { 141 | rsltHtml = '' + result + '' 142 | } else { 143 | return rsltHtml 144 | } 145 | return rsltHtml 146 | } 147 | 148 | function parseValue (isKey = false) { 149 | skipSpace() 150 | if (isKey) { 151 | if (state.jsonStr[i] === `"`) { 152 | i++ 153 | return parseString() 154 | } else { 155 | return undefined 156 | } 157 | } else { 158 | for (; i < state.jsonStr.length; i++) { 159 | if (state.jsonStr[i] === `{`) { 160 | i++ 161 | return parseObject() 162 | } 163 | if (state.jsonStr[i] === `[`) { 164 | i++ 165 | return parseArray() 166 | } 167 | if (state.jsonStr[i] === `"`) { 168 | i++ 169 | return parseString() 170 | } 171 | if (state.jsonStr[i] === `t` || state.jsonStr[i] === `f`) { 172 | return parseBoolean() 173 | } 174 | if (state.jsonStr[i] === `n`) { 175 | return parserNull() 176 | } 177 | if (/[0-9.e\-+]/i.test(state.jsonStr[i])) { 178 | return parseNumber() 179 | } else { 180 | return undefined 181 | } 182 | } 183 | } 184 | } 185 | 186 | function parseObject () { 187 | let res = {} 188 | let value 189 | skipSpace() 190 | while (state.jsonStr[i] !== `}` && i < state.jsonStr.length) { 191 | let comma = false 192 | 193 | let key = parseValue(true) 194 | if (key === undefined) return errorTermination(i18n.t('json.undefinedKey')) 195 | skipSpace() 196 | if (state.jsonStr[i] !== `:`) return errorTermination(i18n.t('json.missing') + ' : ') 197 | i++ // 跳过`:` 198 | value = parseValue() 199 | if (value === undefined) return errorTermination(i18n.t('json.undefinedVal')) 200 | res[key] = value 201 | skipSpace() 202 | while (/[ ,]/.test(state.jsonStr[i]) && i < state.jsonStr.length - 1) { 203 | comma = true 204 | i++ // 跳过`,` 205 | } 206 | skipSpace() 207 | if (state.jsonStr[i] === `}` && comma) return errorTermination(i18n.t('json.missingKV')) 208 | if (state.jsonStr[i] !== `}` && !comma) return errorTermination(i18n.t('json.missingComma') + ' : ,') 209 | } 210 | 211 | if (state.jsonStr[i] !== `}`) return errorTermination(i18n.t('json.missing') + ' } ...') 212 | i++ // 跳过“}” 213 | return res 214 | } 215 | 216 | function parseArray () { 217 | let res = [] 218 | let value 219 | skipSpace() 220 | while (state.jsonStr[i] !== `]` && i < state.jsonStr.length - 1) { 221 | let comma = false 222 | 223 | value = parseValue() 224 | res.push(value) 225 | skipSpace() 226 | while (/[ ,]/.test(state.jsonStr[i]) && i < state.jsonStr.length - 1) { 227 | comma = true 228 | i++ // 跳过`,` 229 | } 230 | skipSpace() 231 | if (state.jsonStr[i] === `]` && comma) return errorTermination(i18n.t('json.missingElemInArray')) 232 | if (state.jsonStr[i] !== `]` && !comma) return errorTermination(i18n.t('json.missingCommaInArray')) 233 | if (/[ ,]/.test(state.jsonStr[i]) === false && value === undefined) return errorTermination('the element is undefined...') 234 | } 235 | if (state.jsonStr[i] !== `]`) return errorTermination(i18n.t('json.missing') + ' ] ...') 236 | i++ // 跳过结束“]” 237 | return res 238 | } 239 | 240 | function parseString () { 241 | /** 242 | * 直接获取到这串字符串 key 或 value 243 | */ 244 | let res = `` 245 | let j = i 246 | while ((state.jsonStr[i] !== `"` || (state.jsonStr[i] === `"` && state.jsonStr[i - 1] === `\\`)) && i < state.jsonStr.length) { 247 | i++ 248 | } 249 | res = state.jsonStr.slice(j, i) 250 | if (state.jsonStr[i] !== `"`) return errorTermination(i18n.t('json.missing') + ' " ') 251 | i++ // 跳过字符串结尾引号结束`"` 252 | return res 253 | } 254 | 255 | function parseBoolean () { 256 | if (state.jsonStr[i] === `t` && i < state.jsonStr.length) { 257 | if ((i + 3) < state.jsonStr.length && state.jsonStr[i + 1] === `r` && state.jsonStr[i + 2] === `u` && state.jsonStr[i + 3] === `e`) { 258 | i = i + 4 259 | return true 260 | } else { 261 | return i18n.t('json.notBool') 262 | } 263 | } else { 264 | if ((i + 4) < state.jsonStr.length && state.jsonStr[i + 1] === `a` && state.jsonStr[i + 2] === `l` && state.jsonStr[i + 3] === `s` && state.jsonStr[i + 4] === `e`) { 265 | i = i + 5 266 | return false 267 | } else { 268 | return i18n.t('json.notBool') 269 | } 270 | } 271 | } 272 | 273 | function parseNumber () { 274 | let res = `` 275 | let j = i 276 | while (/[0-9.e\-+]/i.test(state.jsonStr[i]) && i < state.jsonStr.length) { 277 | i++ 278 | } 279 | res = Number(state.jsonStr.slice(j, i)) 280 | if (isNaN(res)) return errorTermination(i18n.t('json.notNum')) 281 | return res 282 | } 283 | 284 | function parserNull () { 285 | if ((i + 3) < state.jsonStr.length && state.jsonStr[i] === `n` && state.jsonStr[i + 1] === `u` && state.jsonStr[i + 2] === `l` && state.jsonStr[i + 3] === `l`) { 286 | i = i + 4 287 | return null 288 | } else { 289 | return i18n.t('json.notNull') 290 | } 291 | } 292 | 293 | function skipSpace () { 294 | while (/[ \n\r\t\b]/.test(state.jsonStr[i]) && i < state.jsonStr.length) { 295 | i++ // 跳过空白 296 | } 297 | } 298 | 299 | function errorPosMsg () { 300 | let strPosMsg = state.jsonStr.slice(0, i) 301 | return strPosMsg 302 | } 303 | 304 | function errorTermination (msg) { 305 | state.error = {'code': -1, 'posMsg': errorPosMsg(), 'msg': i18n.t('json.seems') + msg} 306 | return state.error 307 | } 308 | }, 309 | 310 | JSON_TO_XML (state) { 311 | let indentInit = 0 312 | let indentSym = ' ' 313 | let indentCount = 2 314 | let result = state.jsonResult 315 | 316 | state.jsonHtml = '<?xml version="1.0" encoding="UTF-8"?><root>
' 317 | state.jsonHtml += renderXML(result, indentInit) 318 | state.jsonHtml += '</root>' 319 | state.isXML = true 320 | 321 | function renderXML (jsonResult, indtIndx) { 322 | let xmlResult = '' 323 | let indentI = indtIndx + 1 324 | if (Array.isArray(jsonResult)) { 325 | jsonResult.forEach((item, index) => { 326 | if (typeof item === 'object' || Array.isArray(item)) { 327 | xmlResult += ('
' + indentSym.repeat(indentCount * indentI) + '<' + index + '>
') 328 | xmlResult += renderXML(item, indentI) 329 | xmlResult += (indentSym.repeat(indentCount * indentI) + '</' + index + '>
') 330 | } else { 331 | xmlResult += (indentSym.repeat(indentCount * indentI) + '<' + index + '>') 332 | xmlResult += ('' + item + '') 333 | xmlResult += ('</' + index + '>
') 334 | } 335 | }) 336 | } else if (typeof jsonResult === 'object') { 337 | for (let index = 0; index < Object.entries(jsonResult).length; index++) { 338 | let keyval = Object.entries(jsonResult)[index] 339 | xmlResult += (indentSym.repeat(indentCount * indentI) + '<' + keyval[0] + '>') 340 | if (typeof keyval[1] === 'object' || Array.isArray(keyval[1])) { 341 | xmlResult += '
' 342 | xmlResult += renderXML(keyval[1], indentI) 343 | xmlResult += (indentSym.repeat(indentCount * indentI) + '</' + keyval[0] + '>
') 344 | } else { 345 | xmlResult += ('' + keyval[1] + '') 346 | xmlResult += ('</' + keyval[0] + '>
') 347 | } 348 | } 349 | } else { 350 | xmlResult = jsonResult 351 | } 352 | return xmlResult 353 | } 354 | } 355 | } 356 | 357 | const actions = { 358 | setJsonText ({ commit }, value) { 359 | commit('SET_JSON_TEXT', value) 360 | }, 361 | convertJson ({ commit }) { 362 | commit('CONVERT_JSON') 363 | }, 364 | jsonToXML ({ commit }) { 365 | commit('JSON_TO_XML') 366 | } 367 | } 368 | 369 | export default { 370 | state, 371 | mutations, 372 | actions 373 | } 374 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Menu.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | menuState: true, 3 | lang: 'en' 4 | } 5 | 6 | const mutations = { 7 | TOGGLE_MENU (state) { 8 | state.menuState = !state.menuState 9 | }, 10 | TOGGLE_LANG (state, i18n) { 11 | if (i18n.locale === 'cn') { 12 | state.lang = 'en' 13 | } else { 14 | state.lang = 'cn' 15 | } 16 | i18n.locale = state.lang 17 | window.localStorage.setItem('locale', state.lang) 18 | } 19 | } 20 | 21 | const actions = { 22 | toggleMenu ({ commit }) { 23 | // do something async 24 | commit('TOGGLE_MENU') 25 | }, 26 | toggleLang ({ commit }, i18n) { 27 | commit('TOGGLE_LANG', i18n) 28 | } 29 | } 30 | 31 | export default { 32 | state, 33 | mutations, 34 | actions 35 | } 36 | -------------------------------------------------------------------------------- /src/renderer/store/modules/Regex.js: -------------------------------------------------------------------------------- 1 | import i18n from './../../i18n' 2 | 3 | const state = { 4 | tabs: [ 5 | { title: i18n.t('tabs.home'), isActive: true }, 6 | { title: i18n.t('tabs.help'), isActive: false } 7 | ], 8 | currentTab: i18n.t('tabs.home'), 9 | regexExp: '', 10 | regexOpt: '', 11 | regexCont: '', 12 | regexResult: { status: 0, content: i18n.t('regex.heresResult') } 13 | } 14 | 15 | const mutations = { 16 | SET_INIT_STATE (state, payload) { 17 | state.tabs = payload.tabs 18 | state.currentTab = payload.currentTab 19 | state.regexResult = payload.regexResult 20 | }, 21 | SET_NAV (state, payload) { 22 | state.tabs.forEach((tabs, i) => { 23 | if (i === payload.index) { 24 | state.tabs[payload.index]['isActive'] = true 25 | state.currentTab = payload.title 26 | } else { 27 | state.tabs[i]['isActive'] = false 28 | } 29 | }) 30 | }, 31 | CLEAN_FIELDS (state) { 32 | state.regexExp = '' 33 | state.regexOpt = '' 34 | state.regexCont = '' 35 | state.regexResult = { status: 0, content: i18n.t('regex.heresResult') } 36 | }, 37 | REGEX_MATCH (state, target) { 38 | if (target.name === 'regex-exp') { 39 | state.regexExp = target.value 40 | } 41 | if (target.name === 'regex-opt') { 42 | state.regexOpt = target.value 43 | } 44 | if (target.name === 'regex-content') { 45 | state.regexCont = target.value 46 | } 47 | 48 | // Main Match Method: 49 | function matchContext (regexGlobal, result, flag) { 50 | let matchedContext = [] 51 | let matchedGroups = [] 52 | let reGroup = [] 53 | let beginPos = 0 54 | let preventingTag = 0 55 | while ((reGroup = regexGlobal.exec(result['input'])) !== null) { 56 | if (preventingTag > reGroup['index']) break 57 | matchedContext.push(reGroup['input'].slice(beginPos, reGroup['index'])) 58 | matchedContext.push(reGroup[0]) 59 | beginPos = reGroup['index'] + reGroup[0].length 60 | if (flag > 1) matchedGroups.push(reGroup) 61 | preventingTag++ 62 | } 63 | if (beginPos !== result['input'].length) { 64 | matchedContext.push(result['input'].slice(beginPos, result['input'].length)) 65 | } 66 | let regexResult = { status: 1, content: matchedGroups, matchedContext: matchedContext } 67 | return regexResult 68 | } 69 | 70 | // Do the Regex Match: 71 | function execMatchContent (state, regexCont) { 72 | let regexResult 73 | try { 74 | let re = new RegExp(state.regexExp, state.regexOpt) 75 | let reGb = new RegExp(state.regexExp, state.regexOpt + 'g') 76 | let result = regexCont.match(re) 77 | 78 | if (result === null) { 79 | regexResult = { status: -1, content: i18n.t('regex.noMatches'), matchedContext: regexCont } 80 | } else { 81 | if (state.regexExp === '()') { 82 | let matchedContext = [] 83 | regexCont.split('').forEach((val, index) => { 84 | matchedContext.push(val) 85 | matchedContext.push('') 86 | }) 87 | let matchedGroups = [...Array(regexCont.length)].map(() => [regexCont, '']) 88 | regexResult = { status: 1, content: matchedGroups, matchedContext: matchedContext } 89 | } else { 90 | regexResult = matchContext(reGb, result, result.length) 91 | } 92 | } 93 | } catch (err) { 94 | console.log(err.message) 95 | if (err.message.match(/expression/)) { 96 | if (err.message.match(/Unterminated\s+group/)) { 97 | regexResult = { status: -1, content: i18n.t('regex.uHaveAnUnmatchedPats'), matchedContext: regexCont } 98 | } else { 99 | regexResult = { status: -1, content: i18n.t('regex.expError'), matchedContext: regexCont } 100 | } 101 | } else if (err.message.match(/constructor/)) { 102 | regexResult = { status: -1, content: i18n.t('regex.invalidOpt'), matchedContext: regexCont } 103 | } else { 104 | regexResult = { status: -1, content: i18n.t('regex.noMatches'), matchedContext: regexCont } 105 | } 106 | } 107 | return regexResult 108 | } 109 | 110 | if (state.regexCont === '' || state.regexExp === '') { 111 | state.regexResult = { status: 0, content: i18n.t('regex.heresResult') } 112 | } else { 113 | let regexResults = [] 114 | // loop more sample test content 115 | state.regexCont.split('\n').forEach((content) => { 116 | regexResults.push(execMatchContent(state, content)) 117 | }) 118 | let matchedContexts = [] 119 | let contents = [] 120 | let errorFlag = 1 121 | console.log(regexResults) 122 | regexResults.forEach((result) => { 123 | if (result['status'] === 1) { 124 | if (result['content'].length > 0) contents = contents.concat(result['content']) 125 | errorFlag = 0 126 | } 127 | matchedContexts.push(result['matchedContext']) 128 | }) 129 | if (errorFlag === 0) { 130 | state.regexResult = { status: 1, content: contents, matchedContext: matchedContexts } 131 | } else { 132 | state.regexResult = regexResults[0] 133 | } 134 | console.log(state.regexResult) 135 | } 136 | } 137 | } 138 | 139 | const actions = { 140 | setInitState ({ commit }, payload) { 141 | commit('SET_INIT_STATE', payload) 142 | }, 143 | setNav ({ commit }, payload) { 144 | commit('SET_NAV', payload) 145 | }, 146 | cleanFields ({ commit }) { 147 | commit('CLEAN_FIELDS') 148 | }, 149 | regexMatch ({ commit }, payload) { 150 | commit('REGEX_MATCH', payload.target) 151 | } 152 | } 153 | 154 | export default { 155 | state, 156 | mutations, 157 | actions 158 | } 159 | -------------------------------------------------------------------------------- /src/renderer/store/modules/TStamp.js: -------------------------------------------------------------------------------- 1 | import * as moment from 'moment' 2 | import i18n from './../../i18n' 3 | 4 | const state = { 5 | tabs: [ 6 | { title: i18n.t('tabs.home'), isActive: true }, 7 | { title: i18n.t('tabs.help'), isActive: false } 8 | ], 9 | currentTab: i18n.t('tabs.home'), 10 | timestampField: '', 11 | datetimeField: '', 12 | unit: 1, 13 | datetimeResult: '', 14 | timestampResult: '' 15 | } 16 | 17 | const mutations = { 18 | SET_INIT_STATE (state, payload) { 19 | state.tabs = payload.tabs 20 | state.currentTab = payload.currentTab 21 | }, 22 | SET_NAV (state, payload) { 23 | state.tabs.forEach((tabs, i) => { 24 | if (i === payload.index) { 25 | state.tabs[payload.index]['isActive'] = true 26 | state.currentTab = payload.title 27 | } else { 28 | state.tabs[i]['isActive'] = false 29 | } 30 | }) 31 | }, 32 | 33 | RESET_INPUT (state) { 34 | state.timestampField = '' 35 | state.datetimeField = '' 36 | state.datetimeResult = '' 37 | state.timestampResult = '' 38 | }, 39 | 40 | SET_UNIT (state, value) { 41 | state.unit = value 42 | }, 43 | 44 | TIMESTAMP_CONVERT (state, value) { 45 | if (value === '') { 46 | state.datetimeResult = '' 47 | } else { 48 | state.timestampField = value 49 | try { 50 | let timeStamp = state.timestampField 51 | if (typeof state.timestampField !== 'number') { 52 | timeStamp = Number(state.timestampField) 53 | } 54 | let result = moment.unix(timeStamp / state.unit).format('YYYY-MM-DD HH:mm:ss') 55 | state.datetimeResult = result 56 | } catch (err) { 57 | console.log(err.message) 58 | let result = 'Convert Error' 59 | state.datetimeResult = result 60 | } 61 | } 62 | }, 63 | 64 | DATETIME_CONVERT (state, value) { 65 | if (value === '') { 66 | state.timestampResult = '' 67 | } else { 68 | state.datetimeField = value 69 | try { 70 | state.unit.toString() === '1000' ? state.timestampResult = moment(state.datetimeField).valueOf().toString() : state.timestampResult = moment(state.datetimeField).format('X') 71 | } catch (err) { 72 | console.log(err.message) 73 | let result = 'Convert Error' 74 | state.timestampResult = result 75 | } 76 | } 77 | } 78 | } 79 | 80 | const actions = { 81 | setInitState ({ commit }, payload) { 82 | commit('SET_INIT_STATE', payload) 83 | }, 84 | 85 | setNav ({ commit }, payload) { 86 | commit('SET_NAV', payload) 87 | }, 88 | 89 | resetInput ({ commit }) { 90 | commit('RESET_INPUT') 91 | }, 92 | 93 | setUnit ({ commit }, payload) { 94 | commit('SET_UNIT', payload.target.value) 95 | }, 96 | 97 | timestampConvert ({ commit }, payload) { 98 | commit('TIMESTAMP_CONVERT', payload.target.value) 99 | }, 100 | datetimeConvert ({ commit }, payload) { 101 | commit('DATETIME_CONVERT', payload.target.value) 102 | } 103 | } 104 | 105 | export default { 106 | state, 107 | mutations, 108 | actions 109 | } 110 | -------------------------------------------------------------------------------- /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 | modules[key.replace(/(\.\/|\.js)/g, '')]['namespaced'] = true 13 | }) 14 | 15 | export default modules 16 | -------------------------------------------------------------------------------- /static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TsaiKoga/it-tools/0df87d3c1e086106cc36f0cd0357812ea779aff2/static/.gitkeep -------------------------------------------------------------------------------- /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('it-tools') 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('.g-container .g-view .g-text').textContent).to.contain('IT Tools') 12 | }) 13 | }) 14 | --------------------------------------------------------------------------------