| @ -0,0 +1,18 @@ | |||
| { | |||
| "presets": [ | |||
| ["env", { | |||
| "modules": false, | |||
| "targets": { | |||
| "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] | |||
| } | |||
| }], | |||
| "stage-2" | |||
| ], | |||
| "plugins": ["transform-vue-jsx", "transform-runtime"], | |||
| "env": { | |||
| "test": { | |||
| "presets": ["env", "stage-2"], | |||
| "plugins": ["transform-vue-jsx", "transform-es2015-modules-commonjs", "dynamic-import-node"] | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,9 @@ | |||
| root = true | |||
| [*] | |||
| charset = utf-8 | |||
| indent_style = space | |||
| indent_size = 2 | |||
| end_of_line = lf | |||
| insert_final_newline = true | |||
| trim_trailing_whitespace = true | |||
| @ -0,0 +1,10 @@ | |||
| // https://github.com/michael-ciniawsky/postcss-load-config | |||
| module.exports = { | |||
| "plugins": { | |||
| "postcss-import": {}, | |||
| "postcss-url": {}, | |||
| // to edit target browsers: use "browserslist" field in package.json | |||
| "autoprefixer": {} | |||
| } | |||
| } | |||
| @ -0,0 +1,41 @@ | |||
| 'use strict' | |||
| require('./check-versions')() | |||
| process.env.NODE_ENV = 'production' | |||
| const ora = require('ora') | |||
| const rm = require('rimraf') | |||
| const path = require('path') | |||
| const chalk = require('chalk') | |||
| const webpack = require('webpack') | |||
| const config = require('../config') | |||
| const webpackConfig = require('./webpack.prod.conf') | |||
| const spinner = ora('building for production...') | |||
| spinner.start() | |||
| rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { | |||
| if (err) throw err | |||
| webpack(webpackConfig, (err, stats) => { | |||
| spinner.stop() | |||
| if (err) throw err | |||
| process.stdout.write(stats.toString({ | |||
| colors: true, | |||
| modules: false, | |||
| children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. | |||
| chunks: false, | |||
| chunkModules: false | |||
| }) + '\n\n') | |||
| if (stats.hasErrors()) { | |||
| console.log(chalk.red(' Build failed with errors.\n')) | |||
| process.exit(1) | |||
| } | |||
| console.log(chalk.cyan(' Build complete.\n')) | |||
| console.log(chalk.yellow( | |||
| ' Tip: built files are meant to be served over an HTTP server.\n' + | |||
| ' Opening index.html over file:// won\'t work.\n' | |||
| )) | |||
| }) | |||
| }) | |||
| @ -0,0 +1,54 @@ | |||
| 'use strict' | |||
| const chalk = require('chalk') | |||
| const semver = require('semver') | |||
| const packageConfig = require('../package.json') | |||
| const shell = require('shelljs') | |||
| function exec (cmd) { | |||
| return require('child_process').execSync(cmd).toString().trim() | |||
| } | |||
| const versionRequirements = [ | |||
| { | |||
| name: 'node', | |||
| currentVersion: semver.clean(process.version), | |||
| versionRequirement: packageConfig.engines.node | |||
| } | |||
| ] | |||
| if (shell.which('npm')) { | |||
| versionRequirements.push({ | |||
| name: 'npm', | |||
| currentVersion: exec('npm --version'), | |||
| versionRequirement: packageConfig.engines.npm | |||
| }) | |||
| } | |||
| module.exports = function () { | |||
| const warnings = [] | |||
| for (let i = 0; i < versionRequirements.length; i++) { | |||
| const mod = versionRequirements[i] | |||
| if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { | |||
| warnings.push(mod.name + ': ' + | |||
| chalk.red(mod.currentVersion) + ' should be ' + | |||
| chalk.green(mod.versionRequirement) | |||
| ) | |||
| } | |||
| } | |||
| if (warnings.length) { | |||
| console.log('') | |||
| console.log(chalk.yellow('To use this template, you must update following to modules:')) | |||
| console.log() | |||
| for (let i = 0; i < warnings.length; i++) { | |||
| const warning = warnings[i] | |||
| console.log(' ' + warning) | |||
| } | |||
| console.log() | |||
| process.exit(1) | |||
| } | |||
| } | |||
| @ -0,0 +1,102 @@ | |||
| 'use strict' | |||
| const path = require('path') | |||
| const config = require('../config') | |||
| const ExtractTextPlugin = require('extract-text-webpack-plugin') | |||
| const packageConfig = require('../package.json') | |||
| exports.assetsPath = function (_path) { | |||
| const assetsSubDirectory = process.env.NODE_ENV === 'production' | |||
| ? config.build.assetsSubDirectory | |||
| : config.dev.assetsSubDirectory | |||
| return path.posix.join(assetsSubDirectory, _path) | |||
| } | |||
| exports.cssLoaders = function (options) { | |||
| options = options || {} | |||
| const cssLoader = { | |||
| loader: 'css-loader', | |||
| options: { | |||
| sourceMap: options.sourceMap | |||
| } | |||
| } | |||
| const postcssLoader = { | |||
| loader: 'postcss-loader', | |||
| options: { | |||
| sourceMap: options.sourceMap | |||
| } | |||
| } | |||
| // generate loader string to be used with extract text plugin | |||
| function generateLoaders (loader, loaderOptions) { | |||
| const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] | |||
| if (loader) { | |||
| loaders.push({ | |||
| loader: loader + '-loader', | |||
| options: Object.assign({}, loaderOptions, { | |||
| sourceMap: options.sourceMap | |||
| }) | |||
| }) | |||
| } | |||
| // Extract CSS when that option is specified | |||
| // (which is the case during production build) | |||
| if (options.extract) { | |||
| return ExtractTextPlugin.extract({ | |||
| use: loaders, | |||
| fallback: 'vue-style-loader', | |||
| publicPath:'../../' | |||
| }) | |||
| } else { | |||
| return ['vue-style-loader'].concat(loaders) | |||
| } | |||
| } | |||
| // https://vue-loader.vuejs.org/en/configurations/extract-css.html | |||
| return { | |||
| css: generateLoaders(), | |||
| postcss: generateLoaders(), | |||
| less: generateLoaders('less'), | |||
| sass: generateLoaders('sass', { indentedSyntax: true }), | |||
| scss: generateLoaders('sass'), | |||
| stylus: generateLoaders('stylus'), | |||
| styl: generateLoaders('stylus') | |||
| } | |||
| } | |||
| // Generate loaders for standalone style files (outside of .vue) | |||
| exports.styleLoaders = function (options) { | |||
| const output = [] | |||
| const loaders = exports.cssLoaders(options) | |||
| for (const extension in loaders) { | |||
| const loader = loaders[extension] | |||
| output.push({ | |||
| test: new RegExp('\\.' + extension + '$'), | |||
| use: loader | |||
| }) | |||
| } | |||
| return output | |||
| } | |||
| exports.createNotifierCallback = () => { | |||
| const notifier = require('node-notifier') | |||
| return (severity, errors) => { | |||
| if (severity !== 'error') return | |||
| const error = errors[0] | |||
| const filename = error.file && error.file.split('!').pop() | |||
| notifier.notify({ | |||
| title: packageConfig.name, | |||
| message: severity + ': ' + error.name, | |||
| subtitle: filename || '', | |||
| icon: path.join(__dirname, 'logo.png') | |||
| }) | |||
| } | |||
| } | |||
| @ -0,0 +1,22 @@ | |||
| 'use strict' | |||
| const utils = require('./utils') | |||
| const config = require('../config') | |||
| const isProduction = process.env.NODE_ENV === 'production' | |||
| const sourceMapEnabled = isProduction | |||
| ? config.build.productionSourceMap | |||
| : config.dev.cssSourceMap | |||
| module.exports = { | |||
| loaders: utils.cssLoaders({ | |||
| sourceMap: sourceMapEnabled, | |||
| extract: isProduction | |||
| }), | |||
| cssSourceMap: sourceMapEnabled, | |||
| cacheBusting: config.dev.cacheBusting, | |||
| transformToRequire: { | |||
| video: ['src', 'poster'], | |||
| source: 'src', | |||
| img: 'src', | |||
| image: 'xlink:href' | |||
| } | |||
| } | |||
| @ -0,0 +1,106 @@ | |||
| 'use strict' | |||
| const path = require('path') | |||
| const utils = require('./utils') | |||
| const config = require('../config') | |||
| const vueLoaderConfig = require('./vue-loader.conf') | |||
| function resolve(dir) { | |||
| return path.join(__dirname, '..', dir) | |||
| } | |||
| module.exports = { | |||
| context: path.resolve(__dirname, '../'), | |||
| entry: { | |||
| // app: './src/main.js' | |||
| app: ['babel-polyfill','./src/main.js'] | |||
| }, | |||
| output: { | |||
| path: config.build.assetsRoot, | |||
| filename: '[name].js', | |||
| publicPath: process.env.NODE_ENV === 'production' ? | |||
| config.build.assetsPublicPath : | |||
| config.dev.assetsPublicPath | |||
| }, | |||
| resolve: { | |||
| extensions: ['.js', '.vue', '.json'], | |||
| alias: { | |||
| 'vue$': 'vue/dist/vue.esm.js', | |||
| '@': resolve('src'), | |||
| 'jquery': path.resolve(__dirname, '../node_modules/jquery/src/jquery') | |||
| } | |||
| }, | |||
| module: { | |||
| rules: [{ | |||
| test: /\.vue$/, | |||
| loader: 'vue-loader', | |||
| options: vueLoaderConfig | |||
| }, | |||
| { | |||
| test: /\.js$/, | |||
| loader: 'babel-loader', | |||
| include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] | |||
| }, | |||
| { | |||
| test: /\.js$/, | |||
| loader: 'babel-loader', | |||
| include: [resolve('src'), resolve('test'), resolve('node_modules/@jiaminghi/data-view/lib')] | |||
| }, | |||
| { | |||
| test: /\.js$/, | |||
| loader: 'babel-loader', | |||
| include: [resolve('src'),resolve('test'),resolve('node_modules/element-ui/src'),resolve('/node_modules/element-ui/packages') | |||
| ] | |||
| }, | |||
| { | |||
| test: /\.js$/, | |||
| loader: 'babel-loader', | |||
| include: [resolve('src'),resolve('test'),resolve('node_modules/element-ui/src'),resolve('/node_modules/vue-baidu-map/components/base') | |||
| ] | |||
| }, | |||
| { | |||
| test: /\.js$/, | |||
| loader: 'babel-loader', | |||
| include: [resolve('src'),resolve('test'),resolve('node_modules/js-base64') | |||
| ] | |||
| }, | |||
| { | |||
| test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, | |||
| loader: 'url-loader', | |||
| options: { | |||
| limit: 10000, | |||
| name: utils.assetsPath('img/[name].[hash:7].[ext]') | |||
| } | |||
| }, | |||
| { | |||
| test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, | |||
| loader: 'url-loader', | |||
| options: { | |||
| limit: 10000, | |||
| name: utils.assetsPath('media/[name].[hash:7].[ext]') | |||
| } | |||
| }, | |||
| { | |||
| test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, | |||
| loader: 'url-loader', | |||
| options: { | |||
| limit: 10000, | |||
| name: utils.assetsPath('fonts/[name].[hash:7].[ext]') | |||
| } | |||
| } | |||
| ] | |||
| }, | |||
| node: { | |||
| // prevent webpack from injecting useless setImmediate polyfill because Vue | |||
| // source contains it (although only uses it if it's native). | |||
| setImmediate: false, | |||
| // prevent webpack from injecting mocks to Node native modules | |||
| // that does not make sense for the client | |||
| dgram: 'empty', | |||
| fs: 'empty', | |||
| net: 'empty', | |||
| tls: 'empty', | |||
| child_process: 'empty' | |||
| } | |||
| } | |||
| @ -0,0 +1,96 @@ | |||
| 'use strict' | |||
| const utils = require('./utils') | |||
| const webpack = require('webpack') | |||
| const config = require('../config') | |||
| const merge = require('webpack-merge') | |||
| const path = require('path') | |||
| const baseWebpackConfig = require('./webpack.base.conf') | |||
| const CopyWebpackPlugin = require('copy-webpack-plugin') | |||
| const HtmlWebpackPlugin = require('html-webpack-plugin') | |||
| const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') | |||
| const portfinder = require('portfinder') | |||
| const HOST = process.env.HOST | |||
| const PORT = process.env.PORT && Number(process.env.PORT) | |||
| const devWebpackConfig = merge(baseWebpackConfig, { | |||
| module: { | |||
| rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) | |||
| }, | |||
| // cheap-module-eval-source-map is faster for development | |||
| devtool: config.dev.devtool, | |||
| // these devServer options should be customized in /config/index.js | |||
| devServer: { | |||
| clientLogLevel: 'warning', | |||
| historyApiFallback: { | |||
| rewrites: [ | |||
| { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, | |||
| ], | |||
| }, | |||
| https:false, | |||
| hot: true, | |||
| contentBase: false, // since we use CopyWebpackPlugin. | |||
| compress: true, | |||
| host: HOST || config.dev.host, | |||
| port: PORT || config.dev.port, | |||
| open: config.dev.autoOpenBrowser, | |||
| overlay: config.dev.errorOverlay | |||
| ? { warnings: false, errors: true } | |||
| : false, | |||
| publicPath: config.dev.assetsPublicPath, | |||
| proxy: config.dev.proxyTable, | |||
| quiet: true, // necessary for FriendlyErrorsPlugin | |||
| watchOptions: { | |||
| poll: config.dev.poll, | |||
| } | |||
| }, | |||
| plugins: [ | |||
| new webpack.DefinePlugin({ | |||
| 'process.env': require('../config/dev.env') | |||
| }), | |||
| new webpack.HotModuleReplacementPlugin(), | |||
| new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. | |||
| new webpack.NoEmitOnErrorsPlugin(), | |||
| // https://github.com/ampedandwired/html-webpack-plugin | |||
| new HtmlWebpackPlugin({ | |||
| filename: 'index.html', | |||
| template: 'index.html', | |||
| inject: true | |||
| }), | |||
| // copy custom static assets | |||
| new CopyWebpackPlugin([ | |||
| { | |||
| from: path.resolve(__dirname, '../static'), | |||
| to: config.dev.assetsSubDirectory, | |||
| ignore: ['.*'] | |||
| } | |||
| ]) | |||
| ] | |||
| }) | |||
| module.exports = new Promise((resolve, reject) => { | |||
| portfinder.basePort = process.env.PORT || config.dev.port | |||
| portfinder.getPort((err, port) => { | |||
| if (err) { | |||
| reject(err) | |||
| } else { | |||
| // publish the new Port, necessary for e2e tests | |||
| process.env.PORT = port | |||
| // add port to devServer config | |||
| devWebpackConfig.devServer.port = port | |||
| // Add FriendlyErrorsPlugin | |||
| devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ | |||
| compilationSuccessInfo: { | |||
| messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], | |||
| }, | |||
| onErrors: config.dev.notifyOnErrors | |||
| ? utils.createNotifierCallback() | |||
| : undefined | |||
| })) | |||
| resolve(devWebpackConfig) | |||
| } | |||
| }) | |||
| }) | |||
| @ -0,0 +1,140 @@ | |||
| 'use strict' | |||
| const path = require('path') | |||
| const utils = require('./utils') | |||
| const webpack = require('webpack') | |||
| const config = require('../config') | |||
| const merge = require('webpack-merge') | |||
| const baseWebpackConfig = require('./webpack.base.conf') | |||
| const CopyWebpackPlugin = require('copy-webpack-plugin') | |||
| const HtmlWebpackPlugin = require('html-webpack-plugin') | |||
| const ExtractTextPlugin = require('extract-text-webpack-plugin') | |||
| const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') | |||
| const env = process.env.NODE_ENV === 'testing' | |||
| ? require('../config/test.env') | |||
| : require('../config/prod.env') | |||
| const webpackConfig = merge(baseWebpackConfig, { | |||
| module: { | |||
| rules: utils.styleLoaders({ | |||
| sourceMap: config.build.productionSourceMap, | |||
| extract: true, | |||
| usePostCSS: true | |||
| }) | |||
| }, | |||
| devtool: config.build.productionSourceMap ? config.build.devtool : false, | |||
| output: { | |||
| publicPath: '/', | |||
| path: config.build.assetsRoot, | |||
| filename: utils.assetsPath('js/[name].[chunkhash].js'), | |||
| chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') | |||
| }, | |||
| plugins: [ | |||
| // http://vuejs.github.io/vue-loader/en/workflow/production.html | |||
| new webpack.DefinePlugin({ | |||
| 'process.env': env | |||
| }), | |||
| // extract css into its own file | |||
| new ExtractTextPlugin({ | |||
| filename: utils.assetsPath('css/[name].[contenthash].css'), | |||
| // Setting the following option to `false` will not extract CSS from codesplit chunks. | |||
| // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. | |||
| // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, | |||
| // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 | |||
| allChunks: true, | |||
| }), | |||
| // Compress extracted CSS. We are using this plugin so that possible | |||
| // duplicated CSS from different components can be deduped. | |||
| new OptimizeCSSPlugin({ | |||
| cssProcessorOptions: config.build.productionSourceMap | |||
| ? { safe: true, map: { inline: false } } | |||
| : { safe: true } | |||
| }), | |||
| // generate dist index.html with correct asset hash for caching. | |||
| // you can customize output by editing /index.html | |||
| // see https://github.com/ampedandwired/html-webpack-plugin | |||
| new HtmlWebpackPlugin({ | |||
| filename: process.env.NODE_ENV === 'testing' | |||
| ? 'index.html' | |||
| : config.build.index, | |||
| template: 'index.html', | |||
| inject: true, | |||
| minify: { | |||
| removeComments: true, | |||
| collapseWhitespace: true, | |||
| removeAttributeQuotes: true | |||
| // more options: | |||
| // https://github.com/kangax/html-minifier#options-quick-reference | |||
| }, | |||
| // necessary to consistently work with multiple chunks via CommonsChunkPlugin | |||
| chunksSortMode: 'dependency' | |||
| }), | |||
| // keep module.id stable when vendor modules does not change | |||
| new webpack.HashedModuleIdsPlugin(), | |||
| // enable scope hoisting | |||
| new webpack.optimize.ModuleConcatenationPlugin(), | |||
| // split vendor js into its own file | |||
| new webpack.optimize.CommonsChunkPlugin({ | |||
| name: 'vendor', | |||
| minChunks (module) { | |||
| // any required modules inside node_modules are extracted to vendor | |||
| return ( | |||
| module.resource && | |||
| /\.js$/.test(module.resource) && | |||
| module.resource.indexOf( | |||
| path.join(__dirname, '../node_modules') | |||
| ) === 0 | |||
| ) | |||
| } | |||
| }), | |||
| // extract webpack runtime and module manifest to its own file in order to | |||
| // prevent vendor hash from being updated whenever app bundle is updated | |||
| new webpack.optimize.CommonsChunkPlugin({ | |||
| name: 'manifest', | |||
| minChunks: Infinity | |||
| }), | |||
| // This instance extracts shared chunks from code splitted chunks and bundles them | |||
| // in a separate chunk, similar to the vendor chunk | |||
| // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk | |||
| new webpack.optimize.CommonsChunkPlugin({ | |||
| name: 'app', | |||
| async: 'vendor-async', | |||
| children: true, | |||
| minChunks: 3 | |||
| }), | |||
| // copy custom static assets | |||
| new CopyWebpackPlugin([ | |||
| { | |||
| from: path.resolve(__dirname, '../static'), | |||
| to: config.build.assetsSubDirectory, | |||
| ignore: ['.*'] | |||
| } | |||
| ]) | |||
| ] | |||
| }) | |||
| if (config.build.productionGzip) { | |||
| const CompressionWebpackPlugin = require('compression-webpack-plugin') | |||
| webpackConfig.plugins.push( | |||
| new CompressionWebpackPlugin({ | |||
| asset: '[path].gz[query]', | |||
| algorithm: 'gzip', | |||
| test: new RegExp( | |||
| '\\.(' + | |||
| config.build.productionGzipExtensions.join('|') + | |||
| ')$' | |||
| ), | |||
| threshold: 10240, | |||
| minRatio: 0.8 | |||
| }) | |||
| ) | |||
| } | |||
| if (config.build.bundleAnalyzerReport) { | |||
| const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin | |||
| webpackConfig.plugins.push(new BundleAnalyzerPlugin()) | |||
| } | |||
| module.exports = webpackConfig | |||
| @ -0,0 +1,7 @@ | |||
| 'use strict' | |||
| const merge = require('webpack-merge') | |||
| const prodEnv = require('./prod.env') | |||
| module.exports = merge(prodEnv, { | |||
| NODE_ENV: '"development"' | |||
| }) | |||
| @ -0,0 +1,60 @@ | |||
| 'use strict' | |||
| // Template version: 1.3.1 | |||
| // see http://vuejs-templates.github.io/webpack for documentation. | |||
| const path = require('path') | |||
| module.exports = { | |||
| dev: { | |||
| // Paths | |||
| assetsSubDirectory: 'static', | |||
| assetsPublicPath: '/', | |||
| proxyTable: { | |||
| '/api': { | |||
| target: 'http://apartmentcloud.xiaozhisz.cn/', | |||
| // target: 'http://test.zhiweisz.cn/', | |||
| // target: 'http://192.168.1.26:8600/', | |||
| changeOrigin: true, //是否跨域 | |||
| pathRewrite: { | |||
| //重写路径 | |||
| '^/api': '' //代理路径 | |||
| } | |||
| } | |||
| }, | |||
| configureWebpack: { | |||
| resolve: { | |||
| extensions: ['.js', '.vue', '.json'], | |||
| alias: { | |||
| vue$: 'vue/dist/vue.esm.js', | |||
| '@': path.join(__dirname, '/', 'src') | |||
| } | |||
| } | |||
| }, | |||
| host: '192.168.1.20', | |||
| port: 8082, | |||
| autoOpenBrowser: false, | |||
| errorOverlay: true, | |||
| notifyOnErrors: true, | |||
| poll: false, | |||
| devtool: 'cheap-module-eval-source-map', | |||
| cacheBusting: true, | |||
| cssSourceMap: true | |||
| }, | |||
| build: { | |||
| // Template for index.html | |||
| index: path.resolve(__dirname, '../dist/index.html'), | |||
| assetsRoot: path.resolve(__dirname, '../dist'), | |||
| assetsSubDirectory: 'static', | |||
| assetsPublicPath: '/', | |||
| /** | |||
| * Source Maps | |||
| */ | |||
| productionSourceMap: false, | |||
| devtool: '#source-map', | |||
| productionGzip: false, | |||
| productionGzipExtensions: ['js', 'css'], | |||
| bundleAnalyzerReport: process.env.npm_config_report | |||
| } | |||
| } | |||
| @ -0,0 +1,4 @@ | |||
| 'use strict' | |||
| module.exports = { | |||
| NODE_ENV: '"production"' | |||
| } | |||
| @ -0,0 +1,7 @@ | |||
| 'use strict' | |||
| const merge = require('webpack-merge') | |||
| const devEnv = require('./dev.env') | |||
| module.exports = merge(devEnv, { | |||
| NODE_ENV: '"testing"' | |||
| }) | |||
| @ -0,0 +1 @@ | |||
| <!DOCTYPE html><html><head><meta http-equiv=Content-Language><meta name=viewport content="width=device-width,initial-scale=1"><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta http-equiv=Content-Type content="text/html;charset=utf-8"><title>智慧公寓管理系统</title><link href=/static/css/app.4235ce62fb9c140db2020a89f8ca28fc.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.18ec6b5593d846b34fd0.js></script><script type=text/javascript src=/static/js/vendor.9c8489941f3a5aa906bb.js></script><script type=text/javascript src=/static/js/app.509b36048f5247e714bd.js></script></body></html> | |||
| @ -0,0 +1,500 @@ | |||
| <html> | |||
| <head> | |||
| <!-- <meta http-equiv="Content-Language" content="zh-cn"> | |||
| <meta name="GENERATOR" content="Microsoft FrontPage 6.0"> | |||
| <meta name="ProgId" content="FrontPage.Editor.Document"> --> | |||
| <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> | |||
| <title>USB IC 卡读卡器控件调用例程 V1.0</title> | |||
| <script language="javascript"> | |||
| var strls = ""; | |||
| var errorno = ""; | |||
| //控制字定义,控制字指定,控制字的含义请查看本公司网站提供的动态库说明 | |||
| //javascript无法自定义常量, 你可以使用变量代替 | |||
| var BLOCK0_EN = 0x01;//读第一块的(16个字节) | |||
| var BLOCK1_EN = 0x02;//读第二块的(16个字节) | |||
| var BLOCK2_EN = 0x04;//读第三块的(16个字节) | |||
| var NEEDSERIAL = 0x08;//仅读指定序列号的卡 | |||
| var EXTERNKEY = 0x10;//用明码认证密码,产品开发完成后,建议把密码放到设备的只写区,然后用该区的密码后台认证,这样谁都不知道密码是多少,需要这方面支持请联系 | |||
| var NEEDHALT = 0x20;//读/写完卡后立即休眠该卡,相当于这张卡不在感应区。要相重新操作该卡必要拿开卡再放上去 | |||
| var myctrlword = 0; | |||
| var myareano = 0; | |||
| var authmode = 0; | |||
| var mypiccserial = ""; | |||
| var mypicckey = ""; | |||
| var piccdata0_2 = ""; | |||
| var mypicckey_old = "";//旧密码 | |||
| var mypicckey_new = "";//新密码 | |||
| function readcard() | |||
| { | |||
| //指定控制字 | |||
| myctrlword=BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY; | |||
| //指定区号 | |||
| myareano = 8; //指定为第8区 | |||
| //批定密码模式 | |||
| authmode = 1; //大于0表示用A密码认证,推荐用A密码认证 | |||
| //指定序列号,未知卡序列号时可指定为8个0 | |||
| mypiccserial="00000000"; | |||
| //指定密码,以下密码为厂家出厂密码 | |||
| mypicckey = "ffffffffffff"; | |||
| strls=IcCardReader.piccreadex(myctrlword, mypiccserial,myareano,authmode,mypicckey); | |||
| errorno = strls.substr(0,4); | |||
| if(errorno == "ER00"){ | |||
| beep() | |||
| } | |||
| switch(errorno) | |||
| { | |||
| case "ER08": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER09": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER10": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER11": | |||
| CardIDShower.value = "密码认证错误\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER12": | |||
| CardIDShower.value = "密码认证错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER13": | |||
| CardIDShower.value = "读卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("读卡错误"); | |||
| break; | |||
| case "ER14": | |||
| CardIDShower.value = "写卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("写卡错误"); | |||
| break; | |||
| case "ER21": | |||
| alert("没找到动态库"); | |||
| break; | |||
| case "ER22": | |||
| alert("动态库或驱动程序异常"); | |||
| break; | |||
| case "ER23": | |||
| alert("读卡器未插上或动态库或驱动程序异常"); | |||
| break; | |||
| case "ER24": | |||
| alert("操作超时,一般是动态库没有反应"); | |||
| break; | |||
| case "ER25": | |||
| alert("发送字数不够"); | |||
| break; | |||
| case "ER26": | |||
| alert("发送的CRC错"); | |||
| break; | |||
| case "ER27": | |||
| alert("接收的字数不够"); | |||
| break; | |||
| case "ER28": | |||
| alert("接收的CRC错"); | |||
| break; | |||
| case "ER29": | |||
| alert("函数输入参数格式错误,请仔细查看" ); | |||
| break; | |||
| default : | |||
| //读卡成功,其中ER00表示完全成功,ER01表示完全没读到卡数据,ER02表示仅读该卡的第一块成功,,ER02表示仅读该卡的第一二块成功,这是刷卡太快原因 | |||
| CardIDShower.value = "读卡成功" + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "该区第一块十六进制数据为:" + strls.substr(14,32) + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "该区第二块十六进制数据为:" + strls.substr(46,32) + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "该区第三块十六进制数据为:" + strls.substr(78,32) + "\r\n"; | |||
| break; | |||
| } | |||
| } | |||
| function writecard() | |||
| { | |||
| //指定控制字 | |||
| myctrlword=BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY; | |||
| //指定区号 | |||
| myareano = 8; //指定为第8区 | |||
| //批定密码模式 | |||
| authmode = 1; //大于0表示用A密码认证,推荐用A密码认证 | |||
| //指定序列号,未知卡序列号时可指定为8个0 | |||
| mypiccserial="00000000"; | |||
| //指定密码,以下密码为厂家出厂密码 | |||
| mypicckey = "ffffffffffff"; | |||
| //指定写卡内容,长度为48个字节,其中每个字节以两个字符表示为十六进制数 | |||
| piccdata0_2 = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F"; | |||
| strls=IcCardReader.piccwriteex(myctrlword, mypiccserial,myareano,authmode,mypicckey,piccdata0_2); | |||
| errorno = strls.substr(0,4); | |||
| switch(errorno) | |||
| { | |||
| case "ER08": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER09": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER10": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER11": | |||
| CardIDShower.value = "密码认证错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER12": | |||
| CardIDShower.value = "密码认证错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER13": | |||
| CardIDShower.value = "读卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("读卡错误"); | |||
| break; | |||
| case "ER14": | |||
| CardIDShower.value = "写卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| alert("写卡错误"); | |||
| break; | |||
| case "ER21": | |||
| alert("没找到动态库"); | |||
| break; | |||
| case "ER22": | |||
| alert("动态库或驱动程序异常"); | |||
| break; | |||
| case "ER23": | |||
| alert("读卡器未插上或动态库或驱动程序异常"); | |||
| break; | |||
| case "ER24": | |||
| alert("操作超时,一般是动态库没有反应"); | |||
| break; | |||
| case "ER25": | |||
| alert("发送字数不够"); | |||
| break; | |||
| case "ER26": | |||
| alert("发送的CRC错"); | |||
| break; | |||
| case "ER27": | |||
| alert("接收的字数不够"); | |||
| break; | |||
| case "ER28": | |||
| alert("接收的CRC错"); | |||
| break; | |||
| case "ER29": | |||
| alert("函数输入参数格式错误,请仔细查看"); | |||
| break; | |||
| default ://写卡成功,其中ER00表示完全成功,ER01表示完全没写到卡数据,ER02表示仅写该卡的第一块成功,,ER02表示仅写该卡的第一二块成功,这是刷卡太快原因 | |||
| CardIDShower.value = "写卡成功" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5,8) + "\r\n"; | |||
| break; | |||
| } | |||
| } | |||
| function writecarduid() { | |||
| //指定控制字 | |||
| myctrlword = BLOCK0_EN; | |||
| //批定密码模式 | |||
| authmode = 1; //大于0表示用A密码认证,推荐用A密码认证 | |||
| //指定序列号,未知卡序列号时可指定为8个0 | |||
| mypiccserial = "12345678"; | |||
| //指定密码,以下密码为厂家出厂密码 | |||
| mypicckey = "ffffffffffff"; | |||
| //指定写卡内容,长度为48个字节,其中每个字节以两个字符表示为十六进制数 | |||
| piccdata0 = "12345678000102030405060708090A0B0"; | |||
| strls = IcCardReader.piccwriteserial(myctrlword, mypiccserial,authmode, mypicckey, piccdata0); | |||
| errorno = strls.substr(0, 4); | |||
| switch (errorno) { | |||
| case "ER08": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER09": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER10": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER11": | |||
| CardIDShower.value = "密码认证错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5, 8) + "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER12": | |||
| CardIDShower.value = "密码认证错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5, 8) + "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER13": | |||
| CardIDShower.value = "读卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5, 8) + "\r\n"; | |||
| alert("读卡错误"); | |||
| break; | |||
| case "ER14": | |||
| CardIDShower.value = "写卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5, 8) + "\r\n"; | |||
| alert("写卡错误"); | |||
| break; | |||
| case "ER21": | |||
| alert("没找到动态库"); | |||
| break; | |||
| case "ER22": | |||
| alert("动态库或驱动程序异常"); | |||
| break; | |||
| case "ER23": | |||
| alert("读卡器未插上或动态库或驱动程序异常"); | |||
| break; | |||
| case "ER24": | |||
| alert("操作超时,一般是动态库没有反应"); | |||
| break; | |||
| case "ER25": | |||
| alert("发送字数不够"); | |||
| break; | |||
| case "ER26": | |||
| alert("发送的CRC错"); | |||
| break; | |||
| case "ER27": | |||
| alert("接收的字数不够"); | |||
| break; | |||
| case "ER28": | |||
| alert("接收的CRC错"); | |||
| break; | |||
| case "ER29": | |||
| alert("函数输入参数格式错误,请仔细查看"); | |||
| break; | |||
| default://写卡成功,其中ER00表示完全成功,ER01表示完全没写到卡数据,ER02表示仅写该卡的第一块成功,,ER02表示仅写该卡的第一二块成功,这是刷卡太快原因 | |||
| CardIDShower.value = "写UID号成功" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + "卡十六进制序列号为:" + strls.substr(5, 8) + "\r\n"; | |||
| break; | |||
| } | |||
| } | |||
| function changecardkeyex() | |||
| { | |||
| //指定控制字 | |||
| myctrlword= EXTERNKEY; | |||
| //指定区号 | |||
| myareano = 8; //指定为第8区 | |||
| //批定密码模式 | |||
| authmode = 1; //大于0表示用A密码认证,推荐用A密码认证 | |||
| //指定序列号,未知卡序列号时可指定为8个0 | |||
| mypiccserial="00000000"; | |||
| //旧密码 | |||
| mypicckey_old = "ffffffffffff"; | |||
| //新密码 | |||
| mypicckey_new = "ffffffffffffFF078069ffffffffffff";//其中最前面的ffffffffffff为A密码,中间的FF078069为访问控制位,最后面的ffffffffffff为B密码 | |||
| strls=IcCardReader.piccchangesinglekeyex(myctrlword, mypiccserial,myareano,authmode,mypicckey_old,mypicckey_new) | |||
| errorno = strls.substr(0,4); | |||
| if(errorno == "ER00") | |||
| { | |||
| CardIDShower.value = CardIDShower.value + "修改密码成功,卡序列为:" + strls.substr(strls.length - 8,8) + "\r\n"; | |||
| } | |||
| else if(errorno == "ER08") | |||
| { | |||
| alert("寻不到卡"); | |||
| } | |||
| else | |||
| { | |||
| CardIDShower.value = CardIDShower.value + "修改密码:错误:" + errorno + "\r\n"; | |||
| } | |||
| } | |||
| function getdevicenumber() | |||
| { | |||
| strls=IcCardReader.pcdgetdevicenumber(); | |||
| errorno = strls.substr(0,4); | |||
| if(errorno == "ER00") | |||
| { | |||
| CardIDShower.value = CardIDShower.value + "设备硬件号为:" + strls.substr(strls.length - 8,8) + "\r\n"; | |||
| } | |||
| } | |||
| function beep() | |||
| { | |||
| IcCardReader.pcdbeep(100);//100表示响100毫秒 | |||
| } | |||
| function clears1() | |||
| { | |||
| //alert("abc"); | |||
| CardIDShower.value = ""; | |||
| } | |||
| window.onerror=function() | |||
| { | |||
| alert("不好意思,出错了!"); | |||
| return true;//屏蔽系统事件 | |||
| } | |||
| </script> | |||
| <object classid="clsid:05782014-9FF7-468C-BE96-8EDC73084202" id="IcCardReader" viewastext | |||
| width="0" height="0"> | |||
| </object> | |||
| </head> | |||
| <body> | |||
| <table border="0" style="border-collapse: collapse" width="200" height="100"> | |||
| <tr> | |||
| <td width="160"> | |||
| <textarea rows="2" cols="40" id="CardIDShower" name="S1"></textarea> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <!-- <input type="button" value="读卡" onclick="javascript:readcard()" /> --> | |||
| <button onclick="javascript:readcard()">读卡</button> | |||
| </td> | |||
| </tr> | |||
| <!-- <tr> | |||
| <td width="160"> | |||
| <input type="button" value=" 写 卡 " onclick="javascript:writecard()" /> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <input type="button" value="蜂鸣器响" onclick="javascript:beep()" /> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <input type="button" value="更改卡密码" onclick="javascript:changecardkeyex()" /> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <input type="button" value="读取读写器硬件号" onclick="javascript:getdevicenumber()" /> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <input type="button" value="写卡UID号为12345678" onclick="javascript:writecarduid()" /> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <input type="button" value="清空提示" onclick="javascript:clears1()" /> | |||
| </td> | |||
| </tr> --> | |||
| </table> | |||
| <!-- <p> | |||
| <font style="font-size: 9pt">提示:</font></p> | |||
| <p> | |||
| <font style="font-size: 9pt"> 1、运行本例程前,需先注册 IcCardReader 控件,注册方法为:“开始”->“运行”->输入“regsvr32 ***\IcCardReader.ocx”->“确定”。其中“***”为控件所在路径。</font></p> | |||
| <p> | |||
| <font style="font-size: 9pt"> 2、在IE的Internet 属性设定,让浏览器允许运行 ActiveX 控件。</font></p> | |||
| <p> | |||
| <font style="font-size: 9pt"> 3、如果是在服务器端运行本网页,还需在IE的Internet 属性中设定,将服务器网址设为可信站点,否则网页无权运行本地控件。</font></p> --> | |||
| </body> | |||
| </html> | |||
| @ -0,0 +1,213 @@ | |||
| <template> | |||
| <div> | |||
| <!-- <object | |||
| classid="clsid:05782014-9FF7-468C-BE96-8EDC73084202" | |||
| id="IcCardReader" | |||
| viewastext | |||
| width="0" | |||
| height="0" | |||
| ></object> --> | |||
| <table | |||
| border="0" | |||
| style="border-collapse: collapse" | |||
| width="200" | |||
| height="100" | |||
| > | |||
| <tr> | |||
| <td width="160"> | |||
| <textarea rows="2" cols="40" id="CardIDShower" name="S1"></textarea> | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td width="160"> | |||
| <input | |||
| type="button" | |||
| value=" 读 卡 " | |||
| @click="readcard()" | |||
| /> | |||
| </td> | |||
| </tr> | |||
| </table> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| var strls = ""; | |||
| var errorno = ""; | |||
| //控制字定义,控制字指定,控制字的含义请查看本公司网站提供的动态库说明 | |||
| //javascript无法自定义常量, 你可以使用变量代替 | |||
| var BLOCK0_EN = 0x01;//读第一块的(16个字节) | |||
| var BLOCK1_EN = 0x02;//读第二块的(16个字节) | |||
| var BLOCK2_EN = 0x04;//读第三块的(16个字节) | |||
| var NEEDSERIAL = 0x08;//仅读指定序列号的卡 | |||
| var EXTERNKEY = 0x10;//用明码认证密码,产品开发完成后,建议把密码放到设备的只写区,然后用该区的密码后台认证,这样谁都不知道密码是多少,需要这方面支持请联系 | |||
| var NEEDHALT = 0x20;//读/写完卡后立即休眠该卡,相当于这张卡不在感应区。要相重新操作该卡必要拿开卡再放上去 | |||
| var myctrlword = 0; | |||
| var myareano = 0; | |||
| var authmode = 0; | |||
| var mypiccserial = ""; | |||
| var mypicckey = ""; | |||
| var piccdata0_2 = ""; | |||
| var mypicckey_old = "";//旧密码 | |||
| var mypicckey_new = "";//新密码 | |||
| export default { | |||
| name: "index", | |||
| data() { | |||
| return {}; | |||
| }, | |||
| methods: { | |||
| readcard() { | |||
| //指定控制字 | |||
| myctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY; | |||
| //指定区号 | |||
| myareano = 8; //指定为第8区 | |||
| //批定密码模式 | |||
| authmode = 1; //大于0表示用A密码认证,推荐用A密码认证 | |||
| //指定序列号,未知卡序列号时可指定为8个0 | |||
| mypiccserial = "00000000"; | |||
| //指定密码,以下密码为厂家出厂密码 | |||
| mypicckey = "ffffffffffff"; | |||
| var CardIDShower = document.getElementById('CardIDShower') | |||
| strls = window.IcCardReader.piccreadex( | |||
| myctrlword, | |||
| mypiccserial, | |||
| myareano, | |||
| authmode, | |||
| mypicckey | |||
| ); | |||
| errorno = strls.substr(0, 4); | |||
| if (errorno == "ER00") { | |||
| this.beep(); | |||
| } | |||
| switch (errorno) { | |||
| case "ER08": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER09": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER10": | |||
| alert("寻不到卡"); | |||
| break; | |||
| case "ER11": | |||
| CardIDShower.value = "密码认证错误\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + | |||
| "卡十六进制序列号为:" + | |||
| strls.substr(5, 8) + | |||
| "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER12": | |||
| CardIDShower.value = "密码认证错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + | |||
| "卡十六进制序列号为:" + | |||
| strls.substr(5, 8) + | |||
| "\r\n"; | |||
| alert("密码认证错误"); | |||
| break; | |||
| case "ER13": | |||
| CardIDShower.value = "读卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + | |||
| "卡十六进制序列号为:" + | |||
| strls.substr(5, 8) + | |||
| "\r\n"; | |||
| alert("读卡错误"); | |||
| break; | |||
| case "ER14": | |||
| CardIDShower.value = "写卡错误" + "\r\n"; | |||
| CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + | |||
| "卡十六进制序列号为:" + | |||
| strls.substr(5, 8) + | |||
| "\r\n"; | |||
| alert("写卡错误"); | |||
| break; | |||
| case "ER21": | |||
| alert("没找到动态库"); | |||
| break; | |||
| case "ER22": | |||
| alert("动态库或驱动程序异常"); | |||
| break; | |||
| case "ER23": | |||
| alert("读卡器未插上或动态库或驱动程序异常"); | |||
| break; | |||
| case "ER24": | |||
| alert("操作超时,一般是动态库没有反应"); | |||
| break; | |||
| case "ER25": | |||
| alert("发送字数不够"); | |||
| break; | |||
| case "ER26": | |||
| alert("发送的CRC错"); | |||
| break; | |||
| case "ER27": | |||
| alert("接收的字数不够"); | |||
| break; | |||
| case "ER28": | |||
| alert("接收的CRC错"); | |||
| break; | |||
| case "ER29": | |||
| alert("函数输入参数格式错误,请仔细查看"); | |||
| break; | |||
| default: | |||
| //读卡成功,其中ER00表示完全成功,ER01表示完全没读到卡数据,ER02表示仅读该卡的第一块成功,,ER02表示仅读该卡的第一二块成功,这是刷卡太快原因 | |||
| CardIDShower.value = "读卡成功" + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + strls + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "其中错误号为:" + errorno + "\r\n"; | |||
| CardIDShower.value = | |||
| CardIDShower.value + | |||
| "卡十六进制序列号为:" + | |||
| strls.substr(5, 8) + | |||
| "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "该区第一块十六进制数据为:" + strls.substr(14,32) + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "该区第二块十六进制数据为:" + strls.substr(46,32) + "\r\n"; | |||
| // CardIDShower.value = CardIDShower.value + "该区第三块十六进制数据为:" + strls.substr(78,32) + "\r\n"; | |||
| break; | |||
| } | |||
| }, | |||
| beep() { | |||
| IcCardReader.pcdbeep(100); //100表示响100毫秒 | |||
| }, | |||
| clears1() { | |||
| //alert("abc"); | |||
| CardIDShower.value = ""; | |||
| }, | |||
| }, | |||
| }; | |||
| </script> | |||
| <style> | |||
| </style> | |||
| @ -0,0 +1,3 @@ | |||
| %Windir%\SysWOW64\regsvr32 %Windir%\System32\IcCardReader.ocx -u | |||
| %Windir%\SysWOW64\regsvr32 %Windir%\SysWOW64\IcCardReader.ocx -u | |||
| pause | |||
| @ -0,0 +1,3 @@ | |||
| Copy %~dp0\IcCardReader.ocx %Windir%\SysWOW64\ | |||
| %Windir%\SysWOW64\regsvr32 %Windir%\SysWOW64\IcCardReader.ocx | |||
| pause | |||
| @ -0,0 +1,3 @@ | |||
| Copy %~dp0\IcCardReader.ocx %Windir%\System32\ | |||
| %Windir%\SysWOW64\regsvr32 %Windir%\System32\IcCardReader.ocx | |||
| pause | |||
| @ -0,0 +1,3 @@ | |||
| 如果你的系统是32位的x86系统,请运行 复制及注册IcCardReader控件win_32.bat | |||
| 或者 | |||
| 如果你的系统是64位的x64系统,请运行 复制及注册IcCardReader控件win10_64.bat | |||
| @ -0,0 +1,6 @@ | |||
| 1. 安装chrome最新版本 | |||
| 2. chrome 访问 chrome://extensions/ | |||
| 3. 打开开发者模式 | |||
| 4. 加载已解压的扩展程序 选中mafp_serial\plugin文件夹 | |||
| 5. chrome 访问 chrome://apps/ 打开MAFP Serial, 等待出现日志 found valid device XXX | |||
| 6. chrome 打开index.html 进行注册操作 | |||
| @ -0,0 +1,6 @@ | |||
| 1. 安装chrome最新版本 | |||
| 2. chrome 访问 chrome://extensions/ | |||
| 3. 打开开发者模式 | |||
| 4. 加载已解压的扩展程序 选中mafp_serial\plugin文件夹 | |||
| 5. chrome 访问 chrome://apps/ 打开MAFP Serial, 等待出现日志 found valid device XXX | |||
| 6. chrome 打开index.html 进行注册操作 | |||
| @ -0,0 +1,37 @@ | |||
| <?xml version="1.0" encoding="utf-8"?> | |||
| <!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> | |||
| <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" | |||
| viewBox="0 0 180 320" style="enable-background:new 0 0 180 320;" xml:space="preserve"> | |||
| <style type="text/css"> | |||
| .demo__background-path { | |||
| fill: none; | |||
| stroke: #aaa; | |||
| stroke-linecap: round; | |||
| stroke-width: 2px; | |||
| } | |||
| </style> | |||
| <path class="demo__background-path" d="M46.1,214.3c0,0-4.7-15.6,4.1-33.3"/> | |||
| <path class="demo__background-path" d="M53.5,176.8c0,0,18.2-30.3,57.5-13.7"/> | |||
| <path class="demo__background-path" d="M115.8,166.5c0,0,19.1,8.7,19.6,38.4"/> | |||
| <path class="demo__background-path" d="M47.3,221c0,0,3.1-2.1,4.1-4.6s-5.7-20.2,7-36.7c8.5-11,22.2-19,37.9-15.3"/> | |||
| <path class="demo__background-path" d="M102.2,165.4c10.2,2.7,19.5,10.4,23.5,20.2c6.2,15.2,4.9,27.1,4.1,39.4"/> | |||
| <path class="demo__background-path" d="M51.1,226.5c3.3-2.7,5.1-6.3,5.7-10.5c0.5-4-0.3-7.7-0.3-11.7"/> | |||
| <path class="demo__background-path" d="M129.3,200.1"/> | |||
| <path class="demo__background-path" d="M56.3,197.9c3.1-16.8,17.6-29.9,35.1-28.8c17.7,1.1,30.9,14.9,32.8,32.2"/> | |||
| <path class="demo__background-path" d="M124.2,207.9c0.5,9.3,0.5,18.7-2.1,27.7"/> | |||
| <path class="demo__background-path" d="M54.2,231.1c2.1-2.6,4.6-5.1,6.3-8c4.2-6.8,0.9-14.8,1.5-22.3c0.5-7.1,3.4-16.3,10.4-19.7"/> | |||
| <path class="demo__background-path" d="M77.9,178.2c9.3-5.1,22.9-4.7,30.5,3.3"/> | |||
| <path class="demo__background-path" d="M113,186.5c0,0,13.6,18.9,1,54.8"/> | |||
| <path class="demo__background-path" d="M57.3,235.2c0,0,5.7-3.8,9-12.3"/> | |||
| <path class="demo__background-path" d="M111.7,231.5c0,0-4.1,11.5-5.7,13.6"/> | |||
| <path class="demo__background-path" d="M61.8,239.4c9.3-8.4,12.7-19.7,11.8-31.9c-0.9-12.7,3.8-20.6,18.5-21.2"/> | |||
| <path class="demo__background-path" d="M97.3,188.1c8.4,2.7,11,13,11.3,20.8c0.4,11.8-2.5,23.7-7.9,34.1c-0.1,0.1-0.1,0.2-0.2,0.3 | |||
| c-0.4,0.8-0.8,1.5-1.2,2.3c-0.5,0.8-1,1.7-1.5,2.5"/> | |||
| <path class="demo__background-path" d="M66.2,242.5c0,0,15.3-11.1,13.6-34.9"/> | |||
| <path class="demo__background-path" d="M78.7,202.5c1.5-4.6,3.8-9.4,8.9-10.6c13.5-3.2,15.7,13.3,14.6,22.1"/> | |||
| <path class="demo__background-path" d="M102.2,219.7c0,0-1.7,15.6-10.5,28.4"/> | |||
| <path class="demo__background-path" d="M72,244.9c0,0,8.8-9.9,9.9-15.7"/> | |||
| <path class="demo__background-path" d="M84.5,223c0.3-2.6,0.5-5.2,0.7-7.8c0.1-2.1,0.2-4.6-0.1-6.8c-0.3-2.2-1.1-4.3-0.9-6.5c0.5-4.4,7.2-6.9,10.1-3.1c1.7,2.2,1.7,5.3,1.9,7.9c0.4,3.8,0.3,7.6,0,11.4c-1,10.8-5.4,21-11.5,29.9"/> | |||
| <path class="demo__background-path" d="M90,201.2c0,0,4.6,28.1-11.4,45.2"/> | |||
| <path class="demo__background-path" d="M67.3,219C65,188.1,78,180.1,92.7,180.3c18.3,2,23.7,18.3,20,46.7"/> | |||
| </svg> | |||
| @ -0,0 +1,290 @@ | |||
| <!DOCTYPE html> | |||
| <head> | |||
| <link type="text/css" rel="stylesheet" href="jquery.toast.css"> | |||
| <style type="text/css"> | |||
| *, *:before, *:after { | |||
| box-sizing: border-box; | |||
| margin: 0; | |||
| padding: 0; | |||
| } | |||
| .log-text { | |||
| font-size: 14px; | |||
| padding: 10px 0 0 10px; | |||
| color: #666; | |||
| text-align: center; | |||
| margin-bottom: 30px; | |||
| } | |||
| .fprint-container { | |||
| width: 300px; | |||
| height: 252px; | |||
| margin: 0 auto; | |||
| position: relative; | |||
| } | |||
| .demo__fprint { | |||
| width: 300px; | |||
| height: 260px; | |||
| overflow: visible; | |||
| display: block; | |||
| } | |||
| .demo__fprint_bg { | |||
| position: absolute; | |||
| top: 0; | |||
| left: 0; | |||
| width: 297px; | |||
| height: 260px; | |||
| z-index: -1; | |||
| background: url(./fprintBackground.svg) no-repeat; | |||
| background-position: 78px -260px; | |||
| background-size: auto; | |||
| /* background-position: -100px 0; */ | |||
| /* background-size: cover; */ | |||
| } | |||
| .demo__fprint_bg img{position: absolute;width: 147px;top: 50%; | |||
| left: 75px; | |||
| opacity: .9; | |||
| transform: translateY(-50%);} | |||
| .demo__fprint_bg.leave { | |||
| opacity: 0.5; | |||
| } | |||
| .demo__fprint_bg.down { | |||
| opacity: 1; | |||
| } | |||
| .demo__fprint-path { | |||
| stroke-width: 2.5px; | |||
| stroke-linecap: round; | |||
| fill: none; | |||
| stroke: white; | |||
| visibility: hidden; | |||
| transition: opacity 0.5s ease; | |||
| will-change: stroke-dashoffset, stroke-dasharray; | |||
| -webkit-transform: translateZ(0); | |||
| transform: translateZ(0); | |||
| } | |||
| .demo__fprint-path--pinkish { | |||
| stroke: #a94a8c; | |||
| } | |||
| .demo__fprint-path--purplish { | |||
| stroke: #8742cc; | |||
| } | |||
| .demo__fprint-path#demo__elastic-path { | |||
| will-change: opacity; | |||
| opacity: 1; | |||
| } | |||
| .demo__hidden-path { | |||
| display: none; | |||
| } | |||
| .demo__ending-path { | |||
| fill: none; | |||
| stroke-width: 2.5px; | |||
| stroke-dasharray: 60 1000; | |||
| stroke-dashoffset: 61; | |||
| stroke-linecap: round; | |||
| will-change: stroke-dashoffset, stroke-dasharray, opacity; | |||
| -webkit-transform: translateZ(0); | |||
| transform: translateZ(0); | |||
| transition: stroke-dashoffset 1s ease, stroke-dasharray 0.5s linear, opacity 0.75s ease; | |||
| } | |||
| .demo__ending-path--pinkish { | |||
| stroke: #a94a8c; | |||
| } | |||
| .demo__ending-path--purplish { | |||
| stroke: #8742cc; | |||
| } | |||
| .buttons { | |||
| width: 500px; | |||
| display: flex; | |||
| margin: auto; | |||
| align-items: center; | |||
| justify-content: center; | |||
| } | |||
| #enroll-btn { | |||
| width: 140px; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <div class="fprint-container"> | |||
| <div class="demo__fprint_bg leave"><img src="./fprintBackground.svg" alt=""></div> | |||
| <svg class="demo__fprint" viewBox="0 0 180 320"> | |||
| <defs> | |||
| <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%"> | |||
| <stop offset="0%" stop-color="#8742cc"></stop> | |||
| <stop offset="100%" stop-color="#a94a8c"></stop> | |||
| </linearGradient> | |||
| </defs> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--pinkish" d="M46.1,214.3c0,0-4.7-15.6,4.1-33.3" style="stroke-dasharray: 34.235, 36.235; stroke-dashoffset: 35.235px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--purplish" d="M53.5,176.8c0,0,18.2-30.3,57.5-13.7" style="stroke-dasharray: 64.5064, 66.5064; stroke-dashoffset: 65.5064px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--pinkish" d="M115.8,166.5c0,0,19.1,8.7,19.6,38.4" style="stroke-dasharray: 45.1842, 47.1842; stroke-dashoffset: 46.1842px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--pinkish" d="M47.3,221c0,0,3.1-2.1,4.1-4.6s-5.7-20.2,7-36.7c8.5-11,22.2-19,37.9-15.3" style="stroke-dasharray: 88.0272, 90.0272; stroke-dashoffset: 89.0272px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--pinkish" d="M102.2,165.4c10.2,2.7,19.5,10.4,23.5,20.2c6.2,15.2,4.9,27.1,4.1,39.4" style="stroke-dasharray: 72.1106, 74.1106; stroke-dashoffset: 73.1106px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--purplish" d="M51.1,226.5c3.3-2.7,5.1-6.3,5.7-10.5c0.5-4-0.3-7.7-0.3-11.7" style="stroke-dasharray: 23.9625, 25.9625; stroke-dashoffset: 24.9625px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--purplish" d="M56.3,197.9c3.1-16.8,17.6-29.9,35.1-28.8c17.7,1.1,30.9,14.9,32.8,32.2" style="stroke-dasharray: 99.6723, 101.672; stroke-dashoffset: 100.672px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--purplish" d="M124.2,207.9c0.5,9.3,0.5,18.7-2.1,27.7" style="stroke-dasharray: 27.9142, 29.9142; stroke-dashoffset: 28.9142px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--pinkish" d="M54.2,231.1c2.1-2.6,4.6-5.1,6.3-8c4.2-6.8,0.9-14.8,1.5-22.3c0.5-7.1,3.4-16.3,10.4-19.7" style="stroke-dasharray: 56.1771, 58.1771; stroke-dashoffset: 57.1771px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--purplish" d="M77.9,178.2c9.3-5.1,22.9-4.7,30.5,3.3" style="stroke-dasharray: 32.824, 34.824; stroke-dashoffset: 33.824px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--purplish" d="M113,186.5c0,0,13.6,18.9,1,54.8" style="stroke-dasharray: 56.5353, 58.5353; stroke-dashoffset: 57.5353px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--pinkish" d="M57.3,235.2c0,0,5.7-3.8,9-12.3" style="stroke-dasharray: 15.4365, 17.4365; stroke-dashoffset: 16.4365px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--pinkish" d="M111.7,231.5c0,0-4.1,11.5-5.7,13.6" style="stroke-dasharray: 14.7687, 16.7687; stroke-dashoffset: 15.7687px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--purplish" d="M61.8,239.4c9.3-8.4,12.7-19.7,11.8-31.9c-0.9-12.7,3.8-20.6,18.5-21.2" style="stroke-dasharray: 67.9598, 69.9598; stroke-dashoffset: 68.9598px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--pinkish" d="M97.3,188.1c8.4,2.7,11,13,11.3,20.8c0.4,11.8-2.5,23.7-7.9,34.1c-0.1,0.1-0.1,0.2-0.2,0.3 | |||
| c-0.4,0.8-0.8,1.5-1.2,2.3c-0.5,0.8-1,1.7-1.5,2.5" style="stroke-dasharray: 66.4444, 68.4444; stroke-dashoffset: 67.4444px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--purplish" d="M66.2,242.5c0,0,15.3-11.1,13.6-34.9" style="stroke-dasharray: 38.917, 40.917; stroke-dashoffset: 39.917px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--pinkish" d="M78.7,202.5c1.5-4.6,3.8-9.4,8.9-10.6c13.5-3.2,15.7,13.3,14.6,22.1" style="stroke-dasharray: 46.1932, 48.1932; stroke-dashoffset: 47.1932px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--pinkish" d="M102.2,219.7c0,0-1.7,15.6-10.5,28.4" style="stroke-dasharray: 30.5375, 32.5375; stroke-dashoffset: 31.5375px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-backwards demo__fprint-path--pinkish" d="M72,244.9c0,0,8.8-9.9,9.9-15.7" style="stroke-dasharray: 18.7134, 20.7134; stroke-dashoffset: 19.7134px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--pinkish" d="M84.5,223c0.3-2.6,0.5-5.2,0.7-7.8c0.1-2.1,0.2-4.6-0.1-6.8c-0.3-2.2-1.1-4.3-0.9-6.5c0.5-4.4,7.2-6.9,10.1-3.1 | |||
| c1.7,2.2,1.7,5.3,1.9,7.9c0.4,3.8,0.3,7.6,0,11.4c-1,10.8-5.4,21-11.5,29.9" style="stroke-dasharray: 86.4028, 88.4028; stroke-dashoffset: 87.4028px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--removes-forwards demo__fprint-path--purplish" d="M90,201.2c0,0,4.6,28.1-11.4,45.2" style="stroke-dasharray: 48.0853, 50.0853; stroke-dashoffset: 49.0853px; visibility: visible;"></path> | |||
| <path class="demo__fprint-path demo__fprint-path--pinkish" id="demo__elastic-path" d="M67.3,219C65,188.1,78,180.1,92.7,180.3c18.3,2,23.7,18.3,20,46.7" style="stroke-dasharray: 111.304, 113.304; stroke-dashoffset: 112.304px; visibility: visible;"></path> | |||
| <!-- <line id="demo__straight-path" x1="0" y1="151.3" x2="180" y2="151.3"></line> | |||
| <path class="demo__hidden-path" id="demo__arc-to-top" d="M0,148.4c62.3-13.5,122.3-13.5,180,0"></path> | |||
| <path class="demo__hidden-path" id="demo__curve" d="M0,140.2c13.1-10.5,34.7-17,48.5-4.1c5.5,5.2,7.6,12.1,9.2,19.2c2.4,10.5,4.3,21,7.2,31.4c2.4,8.6,4.3,19.6,10.4,26.7c4.3,5,17.7,13.4,23.1,4.8c5.9-9.4,6.8-22.5,9.7-33c4.9-17.8,13-14.6,15.7-14.6c1.8,0,9,2.3,15.4,5.4c6.2,3,11.9,7.7,17.9,11.2c7,4.1,16.5,9.2,22.8,6.6"></path> | |||
| <path class="demo__ending-path demo__ending-path--pinkish" d="M48.4,220c-5.8,4.2-6.9,11.5-7.6,18.1c-0.8,6.7-0.9,14.9-9.9,12.4c-9.1-2.5-14.7-5.4-19.9-13.4c-3.4-5.2-0.4-12.3,2.3-17.2c3.2-5.9,6.8-13,14.5-11.6c3.5,0.6,7.7,3.4,4.5,7.1"></path> | |||
| <path class="demo__ending-path demo__ending-path--pinkish" d="M57.3,235.2c-14.4,9.4-10.3,19.4-17.8,21.1c-5.5,1.3-8.4-7.8-13.8-4.2c-2.6,1.7-5.7,7.7-4.6,10.9c0.7,2,4.1,2,5.8,2.4c3,0.7,8.4,3,7.6,7.2c-0.6,3-5,5.3-2.4,8.7c1.8,2.2,4.7,1.1,6.9,0.3c11.7-4.3,14.5,0.8,16.5,0.9"></path> | |||
| <path class="demo__ending-path demo__ending-path--purplish" d="M79,246c-1.8,2.4-4.9,2.9-7.6,3.2c-2.7,0.3-5.8-0.8-7.7,1.6c-2.9,3.3,0.7,8.2-1.2,12c-1.5,2.8-4.5,2.4-6.9,1.3c-10.1-4.7-33.2-17.5-38.1-2.5c-1.1,3.4-1.9,7.5-1.3,11c0.6,4,5.6,7.9,7.7,2.3c0.8-2.1,3.1-8.6-1-8.9"></path> | |||
| <path class="demo__ending-path demo__ending-path--pinkish" d="M91.8,248c0,0-3.9,6.4-6.2,9.2c-3.8,4.5-7.9,8.9-11.2,13.8c-1.9,2.8-4.4,6.4-3.7,10c0.9,5.2,4.7,12.5,9.7,14.7c5.2,2.2,15.9-4.7,13.1-10.8c-1.4-3-6.3-7.9-10-7.2c-1,0.2-1.8,1-2,2"></path> | |||
| <path class="demo__ending-path demo__ending-path--purplish" d="M114.8,239.4c-2.7,6.1-8.3,12.8-7.8,19.8c0.3,4.6,3.8,7.4,7.8,9.1c8.9,3.8,19.7,0.4,28.6-1.3c8.8-1.7,19.7-3.2,23.7,6.7c2.8,6.8,6.1,14.7,4.4,22.2"></path> | |||
| <path class="demo__ending-path demo__ending-path--pinkish" d="M129.9,224.2c-0.4,7.5-3.1,18,0.7,25c2.8,5.1,14.3,6.3,19.5,7.4c3.7,0.7,8.7,2.2,12-0.5c6.7-5.4,11.1-13.7,14.1-21.6c3.1-8-4.4-12.8-11.1-14.5c-5-1.3-19.1-0.7-21-6.7c-0.9-2.8,1.8-5.9,3.4-7.9"></path> | |||
| <path class="demo__under-curve" d="M0,140.2c13.1-10.5,34.7-17,48.5-4.1c5.5,5.2,7.6,12.1,9.2,19.2c2.4,10.5,4.3,21,7.2,31.4c2.4,8.6,4.3,19.6,10.4,26.7c4.3,5,17.7,13.4,23.1,4.8c5.9-9.4,6.8-22.5,9.7-33c4.9-17.8,13-14.6,15.7-14.6c1.8,0,9,2.3,15.4,5.4c6.2,3,11.9,7.7,17.9,11.2c7,4.1,16.5,9.2,23.8,6.6l0,125.5l-181,0l0,-179.8"></path> --> | |||
| </svg> | |||
| </div> | |||
| <div class="log-text">loading...</div> | |||
| <div class="buttons"> | |||
| <button id="enroll-btn">GX1000采集指纹</button> | |||
| </div> | |||
| <audio src="./press.mp3" id="press"></audio> | |||
| <audio src="./up.mp3" id="up"></audio> | |||
| <script src='jquery.min.js'></script> | |||
| <script src='jquery.toast.min.js'></script> | |||
| <script src='index.js'></script> | |||
| <script src='mafp_sdk.js'></script> | |||
| <script type="text/javascript"> | |||
| function log(msg) { | |||
| document.querySelector(".log-text").innerHTML = msg; | |||
| } | |||
| onload = function() { | |||
| const EnrollCount = 6; | |||
| const EnrollTimeout = 30 * 1000; | |||
| setFprintProgress(0); | |||
| log("初始化..."); | |||
| mafp.init({ | |||
| enrollCount: EnrollCount, | |||
| enrollTimeout: EnrollTimeout | |||
| }).then(() => { | |||
| log("初始化成功."); | |||
| }).catch(e => { | |||
| log("初始化失败."); | |||
| }); | |||
| var up = document.getElementById("up"); | |||
| var press = document.getElementById("press"); | |||
| $("#enroll-btn").click(() => { | |||
| if (mafp.isWorking) { | |||
| mafp.cancelEnroll(); | |||
| $("#enroll-btn").text("注册"); | |||
| log("注册取消"); | |||
| }else { | |||
| log("注册准备中..."); | |||
| mafp.startEnroll((status) => { | |||
| // console.log(status) | |||
| if (status.err) { | |||
| let logText = "注册出错"; | |||
| switch(status.err) { | |||
| case ErrorReceiveDataErr: | |||
| logText = "数据包传输错误"; | |||
| break; | |||
| case ErrorEnrollTimeout: | |||
| logText = "注册超时"; | |||
| break; | |||
| case ErrorDeviceNotFound: | |||
| logText = "设备未找到"; | |||
| break; | |||
| case ErrorEmptyFail: | |||
| logText = "清空数据失败"; | |||
| break; | |||
| default: | |||
| logText = "注册出错:" + status.err; | |||
| } | |||
| log(logText); | |||
| $.toast({text: logText, position: 'top-center', icon: "error", hideAfter: 5000}) | |||
| $("#enroll-btn").text("注册"); | |||
| return; | |||
| }else { | |||
| if(status.state == STATE_WAIT_FINGER_DOWN) { | |||
| up.pause() | |||
| press.play() | |||
| log("请按手指"); | |||
| } | |||
| else if(status.state == STATE_WAIT_FINGER_LEAVE) { | |||
| up.play() | |||
| log("请抬起手指"); | |||
| } | |||
| else if(status.state == STATE_EXTRACT_TEMPLATE) { | |||
| log("提取特征点..."); | |||
| } | |||
| else if(status.state == STATE_DOWNLOAD_TEMPLATE) { | |||
| log("模板传输中..."); | |||
| } | |||
| else if (status.state == STATE_FINGER_DOWN) { | |||
| $(".demo__fprint_bg").addClass("down"); | |||
| }else if (status.state == STATE_FINGER_LEAVE) { | |||
| $(".demo__fprint_bg").removeClass("down"); | |||
| }else if (status.state >= STATE_EXTRACT_TEMPLATE_FAIL) { | |||
| let errText = "提取特征点失败"; | |||
| if (status.state == STATE_EXTRACT_TEMPLATE_DIRTY) { | |||
| errText = "提取特征点失败, 指纹图像太乱"; | |||
| }else if (status.state == STATE_EXTRACT_TEMPLATE_POINTS_FEW) { | |||
| errText = "提取特征点失败, 特征点太少"; | |||
| }else if (status.state == STATE_EXTRACT_TEMPLATE_DIRTY) { | |||
| errText = "提取特征点失败, 合并失败"; | |||
| } | |||
| $.toast({text: errText, position: 'top-center', hideAfter: 5000}) | |||
| } | |||
| } | |||
| if (status.data) { | |||
| //注册完成 模板数据 二进制数据 | |||
| //可封装成 multipart/form-data 上传服务端 | |||
| console.log(status.data,'指纹数据') | |||
| var templateData = new Uint8Array(status.data); | |||
| localStorage.setItem('fingerprint',status.data) | |||
| // console.log(status.data); | |||
| log("注册完成"); | |||
| $("#enroll-btn").text("注册"); | |||
| } | |||
| if (status.data || status.step == EnrollCount && (status.state == 5 || status.state == 6)) { | |||
| setFprintProgress(1); | |||
| }else { | |||
| setFprintProgress((status.step - 1) / EnrollCount); | |||
| } | |||
| }); | |||
| $("#enroll-btn").text("取消"); | |||
| } | |||
| }) | |||
| } | |||
| </script> | |||
| </body> | |||
| @ -0,0 +1,104 @@ | |||
| const TIME_TO_FILL_FPRINT = 2000; //ms | |||
| const $fprintPaths = document.querySelectorAll('.demo__fprint-path'); | |||
| const $endingPaths = document.querySelectorAll('.demo__ending-path'); | |||
| const fprintPathSelector = '.demo__fprint-path'; | |||
| let fprintPaths = []; | |||
| let lastRafCallTimestamp = 0; | |||
| let curFprintPathsOffset = -1; | |||
| let fprintProgressionDirection = 1; | |||
| let isFprintAnimationInProgress = false; | |||
| let isFprintAnimationOver = false; | |||
| let fprintTick = 0; | |||
| let currentFprintProgresss = 0; | |||
| function offsetAllFprintPaths(ratio) { | |||
| fprintPaths.forEach(path => path.offset(ratio)); | |||
| } | |||
| function fprintFrame(timestamp) { | |||
| if (!lastRafCallTimestamp) { | |||
| lastRafCallTimestamp = timestamp; | |||
| window.requestAnimationFrame(fprintFrame); | |||
| return; | |||
| } | |||
| let diff = timestamp - lastRafCallTimestamp; | |||
| fprintTick = (diff / TIME_TO_FILL_FPRINT) * 2; | |||
| lastRafCallTimestamp = timestamp; | |||
| curFprintPathsOffset += fprintTick * fprintProgressionDirection; | |||
| offsetAllFprintPaths(curFprintPathsOffset); | |||
| if (curFprintPathsOffset >= -1 && curFprintPathsOffset <= 1) { | |||
| window.requestAnimationFrame(fprintFrame); | |||
| } | |||
| else if (curFprintPathsOffset > 1) { | |||
| curFprintPathsOffset = curFprintPathsOffset - 2; | |||
| offsetAllFprintPaths(curFprintPathsOffset); | |||
| window.requestAnimationFrame(fprintFrame); | |||
| } | |||
| else if (curFprintPathsOffset < -1) { | |||
| curFprintPathsOffset = curFprintPathsOffset + 2; | |||
| offsetAllFprintPaths(curFprintPathsOffset); | |||
| window.requestAnimationFrame(fprintFrame); | |||
| } | |||
| } | |||
| function fprintFrameToProcess(timestamp) { | |||
| if (!lastRafCallTimestamp) { | |||
| lastRafCallTimestamp = timestamp; | |||
| window.requestAnimationFrame(fprintFrameToProcess); | |||
| return; | |||
| } | |||
| let diff = timestamp - lastRafCallTimestamp; | |||
| fprintTick = (diff / TIME_TO_FILL_FPRINT) * 2; | |||
| lastRafCallTimestamp = timestamp; | |||
| curFprintPathsOffset += fprintTick * fprintProgressionDirection; | |||
| offsetAllFprintPaths(curFprintPathsOffset); | |||
| if (curFprintPathsOffset >= -1 && curFprintPathsOffset <= currentFprintProgresss - 1) { | |||
| window.requestAnimationFrame(fprintFrameToProcess); | |||
| } | |||
| else if (curFprintPathsOffset > currentFprintProgresss - 1) { | |||
| curFprintPathsOffset = currentFprintProgresss - 1; | |||
| offsetAllFprintPaths(curFprintPathsOffset); | |||
| lastRafCallTimestamp = 0; | |||
| } | |||
| } | |||
| function setFprintProgress(progress) { | |||
| if (progress == 0) { | |||
| curFprintPathsOffset = -1; | |||
| offsetAllFprintPaths(curFprintPathsOffset); | |||
| }else { | |||
| currentFprintProgresss = progress; | |||
| window.requestAnimationFrame(fprintFrameToProcess); | |||
| } | |||
| } | |||
| class Path { | |||
| constructor(selector, index) { | |||
| this.index = index; | |||
| this.querySelection = document.querySelectorAll(selector)[index]; | |||
| this.length = this.querySelection.getTotalLength(); | |||
| this.$ = $(selector).eq(index); | |||
| this.setDasharray(); | |||
| this.removesForwards = this.$.hasClass('demo__fprint-path--removes-forwards'); | |||
| } | |||
| setDasharray() { | |||
| this.$.css('stroke-dasharray', `${this.length} ${this.length + 2}`); | |||
| return this; | |||
| } | |||
| offset(ratio) { | |||
| this.$.css('stroke-dashoffset', -this.length * ratio ); | |||
| return this; | |||
| } | |||
| makeVisible() { | |||
| this.$.css('visibility', 'visible'); | |||
| return this; | |||
| } | |||
| } | |||
| $(document).ready(() => { | |||
| for (let i = 0; i < document.querySelectorAll(fprintPathSelector).length; i++) { | |||
| fprintPaths.push(new Path(fprintPathSelector, i)); | |||
| fprintPaths[i].offset(-1).makeVisible(); | |||
| } | |||
| //window.requestAnimationFrame(fprintFrame); | |||
| }) | |||
| @ -0,0 +1,28 @@ | |||
| /** | |||
| * jQuery toast plugin created by Kamran Ahmed copyright MIT license 2014 | |||
| */ | |||
| .jq-toast-wrap { display: block; position: fixed; width: 250px; pointer-events: none !important; margin: 0; padding: 0; letter-spacing: normal; z-index: 9000 !important; } | |||
| .jq-toast-wrap * { margin: 0; padding: 0; } | |||
| .jq-toast-wrap.bottom-left { bottom: 20px; left: 20px; } | |||
| .jq-toast-wrap.bottom-right { bottom: 20px; right: 40px; } | |||
| .jq-toast-wrap.top-left { top: 20px; left: 20px; } | |||
| .jq-toast-wrap.top-right { top: 20px; right: 40px; } | |||
| .jq-toast-single { display: block; width: 100%; padding: 10px; margin: 0px 0px 5px; border-radius: 4px; font-size: 12px; font-family: arial, sans-serif; line-height: 17px; position: relative; pointer-events: all !important; background-color: #444444; color: white; } | |||
| .jq-toast-single h2 { font-family: arial, sans-serif; font-size: 14px; margin: 0px 0px 7px; background: none; color: inherit; line-height: inherit; letter-spacing: normal; } | |||
| .jq-toast-single a { color: #eee; text-decoration: none; font-weight: bold; border-bottom: 1px solid white; padding-bottom: 3px; font-size: 12px; } | |||
| .jq-toast-single ul { margin: 0px 0px 0px 15px; background: none; padding:0px; } | |||
| .jq-toast-single ul li { list-style-type: disc !important; line-height: 17px; background: none; margin: 0; padding: 0; letter-spacing: normal; } | |||
| .close-jq-toast-single { position: absolute; top: 3px; right: 7px; font-size: 14px; cursor: pointer; } | |||
| .jq-toast-loader { display: block; position: absolute; top: -2px; height: 5px; width: 0%; left: 0; border-radius: 5px; background: red; } | |||
| .jq-toast-loaded { width: 100%; } | |||
| .jq-has-icon { padding: 10px 10px 10px 50px; background-repeat: no-repeat; background-position: 10px; } | |||
| .jq-icon-info { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII='); background-color: #31708f; color: #d9edf7; border-color: #bce8f1; } | |||
| .jq-icon-warning { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII='); background-color: #8a6d3b; color: #fcf8e3; border-color: #faebcc; } | |||
| .jq-icon-error { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII='); background-color: #a94442; color: #f2dede; border-color: #ebccd1; } | |||
| .jq-icon-success { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg=='); color: #dff0d8; background-color: #3c763d; border-color: #d6e9c6; } | |||
| @ -0,0 +1,107 @@ | |||
| const STATE_WAIT_FINGER_DOWN = 1; | |||
| const STATE_WAIT_FINGER_LEAVE = 2; | |||
| const STATE_FINGER_DOWN = 3; | |||
| const STATE_FINGER_LEAVE = 4; | |||
| const STATE_EXTRACT_TEMPLATE = 5; | |||
| const STATE_DOWNLOAD_TEMPLATE = 6; | |||
| const STATE_EXTRACT_TEMPLATE_FAIL = 100; | |||
| const STATE_EXTRACT_TEMPLATE_DIRTY = 101; | |||
| const STATE_EXTRACT_TEMPLATE_POINTS_FEW = 102; | |||
| const STATE_EXTRACT_TEMPLATE_MERGE_FAIL = 103; | |||
| const ErrorReceiveDataErr = 1; | |||
| const ErrorEnrollTimeout = 2; | |||
| const ErrorDeviceNotFound = 3; | |||
| const ErrorEmptyFail = 4; | |||
| var mafp = { | |||
| isConnected: false, | |||
| ws: null, | |||
| enrollCount: 6, | |||
| enrollTimeout: 30 * 1000, | |||
| callback: null, | |||
| isWorking: false, | |||
| init(conf) { | |||
| if (conf && conf.enrollCount) { | |||
| this.enrollCount = conf.enrollCount; | |||
| } | |||
| if (conf && conf.enrollTimeout) { | |||
| this.enrollTimeout = conf.enrollTimeout; | |||
| } | |||
| return new Promise((resolve,reject) => { | |||
| var that = this; | |||
| if (this.isConnected) { | |||
| resolve(); | |||
| return; | |||
| } | |||
| var timeout = setTimeout(() => { | |||
| if (!that.isConnected) { | |||
| reject("Connection timeout"); | |||
| } | |||
| }, 2000) | |||
| var port = 9897; | |||
| var address = 'ws://localhost:' + port + '/'; | |||
| that.ws = new WebSocket(address); | |||
| that.ws.addEventListener('open', function() { | |||
| that.isConnected = true; | |||
| if (timeout) { | |||
| clearTimeout(timeout); | |||
| timeout = 0; | |||
| } | |||
| resolve() | |||
| }); | |||
| that.ws.addEventListener('close', function() { | |||
| console.log('Connection lost'); | |||
| that.isConnected = false; | |||
| reject("Connection lost"); | |||
| }); | |||
| that.ws.addEventListener('message', function(e) { | |||
| let resp = JSON.parse(e.data); | |||
| if (that.callback) { | |||
| that.callback(resp); | |||
| if(resp.err || resp.data) { | |||
| that.isWorking = false; | |||
| that.callback = null; | |||
| } | |||
| } | |||
| }); | |||
| }); | |||
| }, | |||
| _sendMessage(msg) { | |||
| this.ws.send(JSON.stringify(msg)); | |||
| }, | |||
| startEnroll(callback=((status, data)=>{})) { | |||
| this.callback = callback; | |||
| this.isWorking = true; | |||
| this.init().then(() => { | |||
| this._sendMessage({ | |||
| cmd: "enrollStart", | |||
| config: { | |||
| enrollCount: this.enrollCount, | |||
| enrollTimeout: this.enrollTimeout | |||
| } | |||
| }); | |||
| }).catch(err => { | |||
| this.callback = null; | |||
| this.isWorking = false; | |||
| callback({ | |||
| err: err, | |||
| state: 0, | |||
| step:0 | |||
| }) | |||
| }) | |||
| }, | |||
| cancelEnroll() { | |||
| this.callback = null; | |||
| this.isWorking = false; | |||
| this._sendMessage({ | |||
| cmd: "enrollCancel" | |||
| }); | |||
| } | |||
| }; | |||
| @ -0,0 +1,29 @@ | |||
| var socketId = 0; | |||
| chrome.app.runtime.onLaunched.addListener(function() { | |||
| if (socketId) { | |||
| console.log("close socket " +socketId); | |||
| chrome.sockets.tcpServer.close(socketId); | |||
| socketId = 0; | |||
| } | |||
| chrome.app.window.create('main.html', { | |||
| id: "mainwin", | |||
| innerBounds: { | |||
| width: 670, | |||
| height: 350, | |||
| minWidth: 670, | |||
| minHeight: 350 | |||
| } | |||
| }); | |||
| }) | |||
| chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) | |||
| { | |||
| socketId = request.socketId; | |||
| }); | |||
| chrome.runtime.onSuspend.addListener(function() { | |||
| if(socketId) { | |||
| chrome.sockets.tcpServer.close(socketId); | |||
| socketId = 0; | |||
| } | |||
| }); | |||
| @ -0,0 +1,17 @@ | |||
| var MyEvent = function(type="") { | |||
| this.type = type; | |||
| this.listeners = []; | |||
| }; | |||
| MyEvent.prototype.addListener = function(listener) { | |||
| this.listeners.push(listener); | |||
| } | |||
| MyEvent.prototype.dispatch = function() { | |||
| for (var listener of this.listeners) { | |||
| var ret = listener.apply(null, arguments); | |||
| if (!ret) { | |||
| return; | |||
| } | |||
| } | |||
| } | |||
| @ -0,0 +1,894 @@ | |||
| /** | |||
| * Copyright (c) 2013 The Chromium Authors. All rights reserved. | |||
| * Use of this source code is governed by a BSD-style license that can be | |||
| * found in the LICENSE file. | |||
| **/ | |||
| var http = function() { | |||
| if (!chrome.sockets || !chrome.sockets.tcpServer) | |||
| return {}; | |||
| // Wrap chrome.sockets.tcp socketId with a Promise API. | |||
| var PSocket = (function() { | |||
| // chrome.sockets.tcp uses a global listener for incoming data so | |||
| // use a map to dispatch to the proper instance. | |||
| var socketMap = {}; | |||
| chrome.sockets.tcp.onReceive.addListener(function(info) { | |||
| var pSocket = socketMap[info.socketId]; | |||
| if (pSocket) { | |||
| if (pSocket.handlers) { | |||
| // Fulfil the pending read. | |||
| pSocket.handlers.resolve(info.data); | |||
| delete pSocket.handlers; | |||
| } | |||
| else { | |||
| // No pending read so put data on the queue. | |||
| pSocket.readQueue.push(info); | |||
| } | |||
| } | |||
| }); | |||
| // Read errors also use a global listener. | |||
| chrome.sockets.tcp.onReceiveError.addListener(function(info) { | |||
| var pSocket = socketMap[info.socketId]; | |||
| if (pSocket) { | |||
| if (pSocket.handlers) { | |||
| // Reject the pending read. | |||
| pSocket.handlers.reject(new Error('chrome.sockets.tcp error ' + info.resultCode)); | |||
| delete pSocket.handlers; | |||
| } | |||
| else { | |||
| // No pending read so put data on the queue. | |||
| pSocket.readQueue.push(info); | |||
| } | |||
| } | |||
| }); | |||
| // PSocket constructor. | |||
| return function(socketId) { | |||
| this.socketId = socketId; | |||
| this.readQueue = []; | |||
| // Register this instance for incoming data processing. | |||
| socketMap[socketId] = this; | |||
| chrome.sockets.tcp.setPaused(socketId, false); | |||
| }; | |||
| })(); | |||
| // Returns a Promise<ArrayBuffer> with read data. | |||
| PSocket.prototype.read = function() { | |||
| var that = this; | |||
| if (this.readQueue.length) { | |||
| // Return data from the queue. | |||
| var info = this.readQueue.shift(); | |||
| if (!info.resultCode) | |||
| return Promise.resolve(info.data); | |||
| else | |||
| return Promise.reject(new Error('chrome.sockets.tcp error ' + info.resultCode)); | |||
| } | |||
| else { | |||
| // The queue is empty so install handlers. | |||
| return new Promise(function(resolve, reject) { | |||
| that.handlers = { resolve: resolve, reject: reject }; | |||
| }); | |||
| } | |||
| }; | |||
| // Returns a Promise<integer> with the number of bytes written. | |||
| PSocket.prototype.write = function(data) { | |||
| var that = this; | |||
| return new Promise(function(resolve, reject) { | |||
| chrome.sockets.tcp.send(that.socketId, data, function(info) { | |||
| if (info && info.resultCode >= 0) | |||
| resolve(info.bytesSent); | |||
| else | |||
| reject(new Error('chrome sockets.tcp error ' + (info && info.resultCode))); | |||
| }); | |||
| }); | |||
| }; | |||
| // Returns a Promise. | |||
| PSocket.prototype.close = function() { | |||
| var that = this; | |||
| return new Promise(function(resolve, reject) { | |||
| chrome.sockets.tcp.disconnect(that.socketId, function() { | |||
| chrome.sockets.tcp.close(that.socketId, resolve); | |||
| }); | |||
| }); | |||
| }; | |||
| // Http response code strings. | |||
| var responseMap = { | |||
| 200: 'OK', | |||
| 301: 'Moved Permanently', | |||
| 304: 'Not Modified', | |||
| 400: 'Bad Request', | |||
| 401: 'Unauthorized', | |||
| 403: 'Forbidden', | |||
| 404: 'Not Found', | |||
| 413: 'Request Entity Too Large', | |||
| 414: 'Request-URI Too Long', | |||
| 500: 'Internal Server Error'}; | |||
| /** | |||
| * Convert from an ArrayBuffer to a string. | |||
| * @param {ArrayBuffer} buffer The array buffer to convert. | |||
| * @return {string} The textual representation of the array. | |||
| */ | |||
| var arrayBufferToString = function(buffer) { | |||
| var array = new Uint8Array(buffer); | |||
| var str = ''; | |||
| for (var i = 0; i < array.length; ++i) { | |||
| str += String.fromCharCode(array[i]); | |||
| } | |||
| return str; | |||
| }; | |||
| /** | |||
| * Convert from an UTF-8 array to UTF-8 string. | |||
| * @param {array} UTF-8 array | |||
| * @return {string} UTF-8 string | |||
| */ | |||
| var ary2utf8 = (function() { | |||
| var patterns = [ | |||
| {pattern: '0xxxxxxx', bytes: 1}, | |||
| {pattern: '110xxxxx', bytes: 2}, | |||
| {pattern: '1110xxxx', bytes: 3}, | |||
| {pattern: '11110xxx', bytes: 4}, | |||
| {pattern: '111110xx', bytes: 5}, | |||
| {pattern: '1111110x', bytes: 6} | |||
| ]; | |||
| patterns.forEach(function(item) { | |||
| item.header = item.pattern.replace(/[^10]/g, ''); | |||
| item.pattern01 = item.pattern.replace(/[^10]/g, '0'); | |||
| item.pattern01 = parseInt(item.pattern01, 2); | |||
| item.mask_length = item.header.length; | |||
| item.data_length = 8 - item.header.length; | |||
| var mask = ''; | |||
| for (var i = 0, len = item.mask_length; i < len; i++) { | |||
| mask += '1'; | |||
| } | |||
| for (var i = 0, len = item.data_length; i < len; i++) { | |||
| mask += '0'; | |||
| } | |||
| item.mask = mask; | |||
| item.mask = parseInt(item.mask, 2); | |||
| }); | |||
| return function(ary) { | |||
| var codes = []; | |||
| var cur = 0; | |||
| while(cur < ary.length) { | |||
| var first = ary[cur]; | |||
| var pattern = null; | |||
| for (var i = 0, len = patterns.length; i < len; i++) { | |||
| if ((first & patterns[i].mask) == patterns[i].pattern01) { | |||
| pattern = patterns[i]; | |||
| break; | |||
| } | |||
| } | |||
| if (pattern == null) { | |||
| throw 'utf-8 decode error'; | |||
| } | |||
| var rest = ary.slice(cur + 1, cur + pattern.bytes); | |||
| cur += pattern.bytes; | |||
| var code = ''; | |||
| code += ('00000000' + (first & (255 ^ pattern.mask)).toString(2)).slice(-pattern.data_length); | |||
| for (var i = 0, len = rest.length; i < len; i++) { | |||
| code += ('00000000' + (rest[i] & parseInt('111111', 2)).toString(2)).slice(-6); | |||
| } | |||
| codes.push(parseInt(code, 2)); | |||
| } | |||
| return String.fromCharCode.apply(null, codes); | |||
| }; | |||
| })(); | |||
| /** | |||
| * Convert from an UTF-8 string to UTF-8 array. | |||
| * @param {string} UTF-8 string | |||
| * @return {array} UTF-8 array | |||
| */ | |||
| var utf82ary = (function() { | |||
| var patterns = [ | |||
| {pattern: '0xxxxxxx', bytes: 1}, | |||
| {pattern: '110xxxxx', bytes: 2}, | |||
| {pattern: '1110xxxx', bytes: 3}, | |||
| {pattern: '11110xxx', bytes: 4}, | |||
| {pattern: '111110xx', bytes: 5}, | |||
| {pattern: '1111110x', bytes: 6} | |||
| ]; | |||
| patterns.forEach(function(item) { | |||
| item.header = item.pattern.replace(/[^10]/g, ''); | |||
| item.mask_length = item.header.length; | |||
| item.data_length = 8 - item.header.length; | |||
| item.max_bit_length = (item.bytes - 1) * 6 + item.data_length; | |||
| }); | |||
| var code2utf8array = function(code) { | |||
| var pattern = null; | |||
| var code01 = code.toString(2); | |||
| for (var i = 0, len = patterns.length; i < len; i++) { | |||
| if (code01.length <= patterns[i].max_bit_length) { | |||
| pattern = patterns[i]; | |||
| break; | |||
| } | |||
| } | |||
| if (pattern == null) { | |||
| throw 'utf-8 encode error'; | |||
| } | |||
| var ary = []; | |||
| for (var i = 0, len = pattern.bytes - 1; i < len; i++) { | |||
| ary.unshift(parseInt('10' + ('000000' + code01.slice(-6)).slice(-6), 2)); | |||
| code01 = code01.slice(0, -6); | |||
| } | |||
| ary.unshift(parseInt(pattern.header + ('00000000' + code01).slice(-pattern.data_length), 2)); | |||
| return ary; | |||
| }; | |||
| return function(str) { | |||
| var codes = []; | |||
| for (var i = 0, len = str.length; i < len; i++) { | |||
| var code = str.charCodeAt(i); | |||
| Array.prototype.push.apply(codes, code2utf8array(code)); | |||
| } | |||
| return codes; | |||
| }; | |||
| })(); | |||
| /** | |||
| * Convert a string to an ArrayBuffer. | |||
| * @param {string} string The string to convert. | |||
| * @return {ArrayBuffer} An array buffer whose bytes correspond to the string. | |||
| */ | |||
| var stringToArrayBuffer = function(string) { | |||
| var buffer = new ArrayBuffer(string.length); | |||
| var bufferView = new Uint8Array(buffer); | |||
| for (var i = 0; i < string.length; i++) { | |||
| bufferView[i] = string.charCodeAt(i); | |||
| } | |||
| return buffer; | |||
| }; | |||
| /** | |||
| * An event source can dispatch events. These are dispatched to all of the | |||
| * functions listening for that event type with arguments. | |||
| * @constructor | |||
| */ | |||
| function EventSource() { | |||
| this.listeners_ = {}; | |||
| }; | |||
| EventSource.prototype = { | |||
| /** | |||
| * Add |callback| as a listener for |type| events. | |||
| * @param {string} type The type of the event. | |||
| * @param {function(Object|undefined): boolean} callback The function to call | |||
| * when this event type is dispatched. Arguments depend on the event | |||
| * source and type. The function returns whether the event was "handled" | |||
| * which will prevent delivery to the rest of the listeners. | |||
| */ | |||
| addEventListener: function(type, callback) { | |||
| if (!this.listeners_[type]) | |||
| this.listeners_[type] = []; | |||
| this.listeners_[type].push(callback); | |||
| }, | |||
| /** | |||
| * Remove |callback| as a listener for |type| events. | |||
| * @param {string} type The type of the event. | |||
| * @param {function(Object|undefined): boolean} callback The callback | |||
| * function to remove from the event listeners for events having type | |||
| * |type|. | |||
| */ | |||
| removeEventListener: function(type, callback) { | |||
| if (!this.listeners_[type]) | |||
| return; | |||
| for (var i = this.listeners_[type].length - 1; i >= 0; i--) { | |||
| if (this.listeners_[type][i] == callback) { | |||
| this.listeners_[type].splice(i, 1); | |||
| } | |||
| } | |||
| }, | |||
| /** | |||
| * Dispatch an event to all listeners for events of type |type|. | |||
| * @param {type} type The type of the event being dispatched. | |||
| * @param {...Object} var_args The arguments to pass when calling the | |||
| * callback function. | |||
| * @return {boolean} Returns true if the event was handled. | |||
| */ | |||
| dispatchEvent: function(type, var_args) { | |||
| if (!this.listeners_[type]) | |||
| return false; | |||
| for (var i = 0; i < this.listeners_[type].length; i++) { | |||
| if (this.listeners_[type][i].apply( | |||
| /* this */ null, | |||
| /* var_args */ Array.prototype.slice.call(arguments, 1))) { | |||
| return true; | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| /** | |||
| * HttpServer provides a lightweight Http web server. Currently it only | |||
| * supports GET requests and upgrading to other protocols (i.e. WebSockets). | |||
| * @constructor | |||
| */ | |||
| function HttpServer() { | |||
| EventSource.apply(this); | |||
| this.readyState_ = 0; | |||
| } | |||
| HttpServer.prototype = { | |||
| __proto__: EventSource.prototype, | |||
| /** | |||
| * Listen for connections on |port| using the interface |host|. | |||
| * @param {number} port The port to listen for incoming connections on. | |||
| * @param {string=} opt_host The host interface to listen for connections on. | |||
| * This will default to 0.0.0.0 if not specified which will listen on | |||
| * all interfaces. | |||
| */ | |||
| listen: function(port, opt_host) { | |||
| var t = this; | |||
| chrome.sockets.tcpServer.create(function(socketInfo) { | |||
| chrome.sockets.tcpServer.onAccept.addListener(function(acceptInfo) { | |||
| if (acceptInfo.socketId === socketInfo.socketId) | |||
| t.readRequestFromSocket_(new PSocket(acceptInfo.clientSocketId)); | |||
| }); | |||
| this.socketId = socketInfo.socketId; | |||
| chrome.runtime.sendMessage({socketId: this.socketId}); | |||
| chrome.sockets.tcpServer.listen( | |||
| socketInfo.socketId, | |||
| opt_host || '0.0.0.0', | |||
| port, | |||
| 50, | |||
| function(result) { | |||
| if (!result) { | |||
| t.readyState_ = 1; | |||
| } | |||
| else { | |||
| console.log( | |||
| 'listen error ' + | |||
| chrome.runtime.lastError.message + | |||
| ' (normal if another instance is already serving requests)'); | |||
| } | |||
| }); | |||
| }); | |||
| }, | |||
| close: function() { | |||
| if (this.socketId) { | |||
| chrome.sockets.tcpServer.close(this.socketId); | |||
| this.socketId = 0; | |||
| } | |||
| }, | |||
| readRequestFromSocket_: function(pSocket) { | |||
| var t = this; | |||
| var requestData = ''; | |||
| var endIndex = 0; | |||
| var onDataRead = function(data) { | |||
| requestData += arrayBufferToString(data).replace(/\r\n/g, '\n'); | |||
| // Check for end of request. | |||
| endIndex = requestData.indexOf('\n\n', endIndex); | |||
| if (endIndex == -1) { | |||
| endIndex = requestData.length - 1; | |||
| return pSocket.read().then(onDataRead); | |||
| } | |||
| var headers = requestData.substring(0, endIndex).split('\n'); | |||
| var headerMap = {}; | |||
| // headers[0] should be the Request-Line | |||
| var requestLine = headers[0].split(' '); | |||
| headerMap['method'] = requestLine[0]; | |||
| headerMap['url'] = requestLine[1]; | |||
| headerMap['Http-Version'] = requestLine[2]; | |||
| for (var i = 1; i < headers.length; i++) { | |||
| requestLine = headers[i].split(':', 2); | |||
| if (requestLine.length == 2) | |||
| headerMap[requestLine[0]] = requestLine[1].trim(); | |||
| } | |||
| var request = new HttpRequest(headerMap, pSocket); | |||
| t.onRequest_(request); | |||
| }; | |||
| pSocket.read().then(onDataRead).catch(function(e) { | |||
| pSocket.close(); | |||
| }); | |||
| }, | |||
| onRequest_: function(request) { | |||
| var type = request.headers['Upgrade'] ? 'upgrade' : 'request'; | |||
| var keepAlive = request.headers['Connection'] == 'keep-alive'; | |||
| if (!this.dispatchEvent(type, request)) | |||
| request.close(); | |||
| else if (keepAlive) | |||
| this.readRequestFromSocket_(request.pSocket_); | |||
| }, | |||
| }; | |||
| // MIME types for common extensions. | |||
| var extensionTypes = { | |||
| 'css': 'text/css', | |||
| 'html': 'text/html', | |||
| 'htm': 'text/html', | |||
| 'jpg': 'image/jpeg', | |||
| 'jpeg': 'image/jpeg', | |||
| 'js': 'text/javascript', | |||
| 'png': 'image/png', | |||
| 'svg': 'image/svg+xml', | |||
| 'txt': 'text/plain'}; | |||
| /** | |||
| * Constructs an HttpRequest object which tracks all of the request headers and | |||
| * socket for an active Http request. | |||
| * @param {Object} headers The HTTP request headers. | |||
| * @param {Object} pSocket The socket to use for the response. | |||
| * @constructor | |||
| */ | |||
| function HttpRequest(headers, pSocket) { | |||
| this.version = 'HTTP/1.1'; | |||
| this.headers = headers; | |||
| this.responseHeaders_ = {}; | |||
| this.headersSent = false; | |||
| this.pSocket_ = pSocket; | |||
| this.writes_ = 0; | |||
| this.bytesRemaining = 0; | |||
| this.finished_ = false; | |||
| this.readyState = 1; | |||
| } | |||
| HttpRequest.prototype = { | |||
| __proto__: EventSource.prototype, | |||
| /** | |||
| * Closes the Http request. | |||
| */ | |||
| close: function() { | |||
| // The socket for keep alive connections will be re-used by the server. | |||
| // Just stop referencing or using the socket in this HttpRequest. | |||
| if (this.headers['Connection'] != 'keep-alive') | |||
| pSocket.close(); | |||
| this.pSocket_ = null; | |||
| this.readyState = 3; | |||
| }, | |||
| /** | |||
| * Write the provided headers as a response to the request. | |||
| * @param {int} responseCode The HTTP status code to respond with. | |||
| * @param {Object} responseHeaders The response headers describing the | |||
| * response. | |||
| */ | |||
| writeHead: function(responseCode, responseHeaders) { | |||
| var headerString = this.version + ' ' + responseCode + ' ' + | |||
| (responseMap[responseCode] || 'Unknown'); | |||
| this.responseHeaders_ = responseHeaders; | |||
| if (this.headers['Connection'] == 'keep-alive') | |||
| responseHeaders['Connection'] = 'keep-alive'; | |||
| if (!responseHeaders['Content-Length'] && responseHeaders['Connection'] == 'keep-alive') | |||
| responseHeaders['Transfer-Encoding'] = 'chunked'; | |||
| for (var i in responseHeaders) { | |||
| headerString += '\r\n' + i + ': ' + responseHeaders[i]; | |||
| } | |||
| headerString += '\r\n\r\n'; | |||
| this.write_(stringToArrayBuffer(headerString)); | |||
| }, | |||
| /** | |||
| * Writes data to the response stream. | |||
| * @param {string|ArrayBuffer} data The data to write to the stream. | |||
| */ | |||
| write: function(data) { | |||
| if (this.responseHeaders_['Transfer-Encoding'] == 'chunked') { | |||
| var newline = '\r\n'; | |||
| var byteLength = (data instanceof ArrayBuffer) ? data.byteLength : data.length; | |||
| var chunkLength = byteLength.toString(16).toUpperCase() + newline; | |||
| var buffer = new ArrayBuffer(chunkLength.length + byteLength + newline.length); | |||
| var bufferView = new Uint8Array(buffer); | |||
| for (var i = 0; i < chunkLength.length; i++) | |||
| bufferView[i] = chunkLength.charCodeAt(i); | |||
| if (data instanceof ArrayBuffer) { | |||
| bufferView.set(new Uint8Array(data), chunkLength.length); | |||
| } else { | |||
| for (var i = 0; i < data.length; i++) | |||
| bufferView[chunkLength.length + i] = data.charCodeAt(i); | |||
| } | |||
| for (var i = 0; i < newline.length; i++) | |||
| bufferView[chunkLength.length + byteLength + i] = newline.charCodeAt(i); | |||
| data = buffer; | |||
| } else if (!(data instanceof ArrayBuffer)) { | |||
| data = stringToArrayBuffer(data); | |||
| } | |||
| this.write_(data); | |||
| }, | |||
| /** | |||
| * Finishes the HTTP response writing |data| before closing. | |||
| * @param {string|ArrayBuffer=} opt_data Optional data to write to the stream | |||
| * before closing it. | |||
| */ | |||
| end: function(opt_data) { | |||
| if (opt_data) | |||
| this.write(opt_data); | |||
| if (this.responseHeaders_['Transfer-Encoding'] == 'chunked') | |||
| this.write(''); | |||
| this.finished_ = true; | |||
| this.checkFinished_(); | |||
| }, | |||
| /** | |||
| * Automatically serve the given |url| request. | |||
| * @param {string} url The URL to fetch the file to be served from. This is | |||
| * retrieved via an XmlHttpRequest and served as the response to the | |||
| * request. | |||
| */ | |||
| serveUrl: function(url) { | |||
| var t = this; | |||
| var xhr = new XMLHttpRequest(); | |||
| xhr.onloadend = function() { | |||
| var type = 'text/plain'; | |||
| if (this.getResponseHeader('Content-Type')) { | |||
| type = this.getResponseHeader('Content-Type'); | |||
| } else if (url.indexOf('.') != -1) { | |||
| var extension = url.substr(url.indexOf('.') + 1); | |||
| type = extensionTypes[extension] || type; | |||
| } | |||
| console.log('Served ' + url); | |||
| var contentLength = this.getResponseHeader('Content-Length'); | |||
| if (xhr.status == 200) | |||
| contentLength = (this.response && this.response.byteLength) || 0; | |||
| t.writeHead(this.status, { | |||
| 'Content-Type': type, | |||
| 'Content-Length': contentLength}); | |||
| t.end(this.response); | |||
| }; | |||
| xhr.open('GET', url, true); | |||
| xhr.responseType = 'arraybuffer'; | |||
| xhr.send(); | |||
| }, | |||
| write_: function(array) { | |||
| var t = this; | |||
| this.bytesRemaining += array.byteLength; | |||
| this.pSocket_.write(array).then(function(bytesWritten) { | |||
| t.bytesRemaining -= bytesWritten; | |||
| t.checkFinished_(); | |||
| }).catch(function(e) { | |||
| console.error(e.message); | |||
| return; | |||
| }); | |||
| }, | |||
| checkFinished_: function() { | |||
| if (!this.finished_ || this.bytesRemaining > 0) | |||
| return; | |||
| this.close(); | |||
| } | |||
| }; | |||
| /** | |||
| * Constructs a server which is capable of accepting WebSocket connections. | |||
| * @param {HttpServer} httpServer The Http Server to listen and handle | |||
| * WebSocket upgrade requests on. | |||
| * @constructor | |||
| */ | |||
| function WebSocketServer(httpServer) { | |||
| EventSource.apply(this); | |||
| httpServer.addEventListener('upgrade', this.upgradeToWebSocket_.bind(this)); | |||
| } | |||
| WebSocketServer.prototype = { | |||
| __proto__: EventSource.prototype, | |||
| upgradeToWebSocket_: function(request) { | |||
| if (request.headers['Upgrade'] != 'websocket' || | |||
| !request.headers['Sec-WebSocket-Key']) { | |||
| return false; | |||
| } | |||
| if (this.dispatchEvent('request', new WebSocketRequest(request))) { | |||
| if (request.pSocket_) | |||
| request.reject(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| }; | |||
| /** | |||
| * Constructs a WebSocket request object from an Http request. This invalidates | |||
| * the Http request's socket and offers accept and reject methods for accepting | |||
| * and rejecting the WebSocket upgrade request. | |||
| * @param {HttpRequest} httpRequest The HTTP request to upgrade. | |||
| */ | |||
| function WebSocketRequest(httpRequest) { | |||
| // We'll assume control of the socket for this request. | |||
| HttpRequest.apply(this, [httpRequest.headers, httpRequest.pSocket_]); | |||
| httpRequest.pSocket_ = null; | |||
| } | |||
| WebSocketRequest.prototype = { | |||
| __proto__: HttpRequest.prototype, | |||
| /** | |||
| * Accepts the WebSocket request. | |||
| * @return {WebSocketServerSocket} The websocket for the accepted request. | |||
| */ | |||
| accept: function() { | |||
| // Construct WebSocket response key. | |||
| var clientKey = this.headers['Sec-WebSocket-Key']; | |||
| var toArray = function(str) { | |||
| var a = []; | |||
| for (var i = 0; i < str.length; i++) { | |||
| a.push(str.charCodeAt(i)); | |||
| } | |||
| return a; | |||
| } | |||
| var toString = function(a) { | |||
| var str = ''; | |||
| for (var i = 0; i < a.length; i++) { | |||
| str += String.fromCharCode(a[i]); | |||
| } | |||
| return str; | |||
| } | |||
| // Magic string used for websocket connection key hashing: | |||
| // http://en.wikipedia.org/wiki/WebSocket | |||
| var magicStr = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; | |||
| // clientKey is base64 encoded key. | |||
| clientKey += magicStr; | |||
| var sha1 = new Sha1(); | |||
| sha1.reset(); | |||
| sha1.update(toArray(clientKey)); | |||
| var responseKey = btoa(toString(sha1.digest())); | |||
| var responseHeader = { | |||
| 'Upgrade': 'websocket', | |||
| 'Connection': 'Upgrade', | |||
| 'Sec-WebSocket-Accept': responseKey}; | |||
| if (this.headers['Sec-WebSocket-Protocol']) | |||
| responseHeader['Sec-WebSocket-Protocol'] = this.headers['Sec-WebSocket-Protocol']; | |||
| this.writeHead(101, responseHeader); | |||
| var socket = new WebSocketServerSocket(this.pSocket_); | |||
| // Detach the socket so that we don't use it anymore. | |||
| this.pSocket_ = 0; | |||
| return socket; | |||
| }, | |||
| /** | |||
| * Rejects the WebSocket request, closing the connection. | |||
| */ | |||
| reject: function() { | |||
| this.close(); | |||
| } | |||
| } | |||
| /** | |||
| * Constructs a WebSocketServerSocket using the given socketId. This should be | |||
| * a socket which has already been upgraded from an Http request. | |||
| * @param {number} socketId The socket id with an active websocket connection. | |||
| */ | |||
| function WebSocketServerSocket(pSocket) { | |||
| this.pSocket_ = pSocket; | |||
| this.readyState = 1; | |||
| EventSource.apply(this); | |||
| this.readFromSocket_(); | |||
| } | |||
| WebSocketServerSocket.prototype = { | |||
| __proto__: EventSource.prototype, | |||
| /** | |||
| * Send |data| on the WebSocket. | |||
| * @param {string|Array.<number>|ArrayBuffer} data The data to send over the WebSocket. | |||
| */ | |||
| send: function(data) { | |||
| // WebSocket must specify opcode when send frame. | |||
| // The opcode for data frame is 1(text) or 2(binary). | |||
| if (typeof data == 'string' || data instanceof String) { | |||
| this.sendFrame_(1, data); | |||
| } else { | |||
| this.sendFrame_(2, data); | |||
| } | |||
| }, | |||
| /** | |||
| * Begin closing the WebSocket. Note that the WebSocket protocol uses a | |||
| * handshake to close the connection, so this call will begin the closing | |||
| * process. | |||
| */ | |||
| close: function() { | |||
| if (this.readyState === 1) { | |||
| this.sendFrame_(8); | |||
| this.readyState = 2; | |||
| } | |||
| }, | |||
| readFromSocket_: function() { | |||
| var t = this; | |||
| var data = []; | |||
| var message = ''; | |||
| var fragmentedOp = 0; | |||
| var fragmentedMessages = []; | |||
| var onDataRead = function(dataBuffer) { | |||
| var a = new Uint8Array(dataBuffer); | |||
| for (var i = 0; i < a.length; i++) | |||
| data.push(a[i]); | |||
| while (data.length) { | |||
| var length_code = -1; | |||
| var data_start = 6; | |||
| var mask; | |||
| var fin = (data[0] & 128) >> 7; | |||
| var op = data[0] & 15; | |||
| if (data.length > 1) | |||
| length_code = data[1] & 127; | |||
| if (length_code > 125) { | |||
| if ((length_code == 126 && data.length > 7) || | |||
| (length_code == 127 && data.length > 14)) { | |||
| if (length_code == 126) { | |||
| length_code = data[2] * 256 + data[3]; | |||
| mask = data.slice(4, 8); | |||
| data_start = 8; | |||
| } else if (length_code == 127) { | |||
| length_code = 0; | |||
| for (var i = 0; i < 8; i++) { | |||
| length_code = length_code * 256 + data[2 + i]; | |||
| } | |||
| mask = data.slice(10, 14); | |||
| data_start = 14; | |||
| } | |||
| } else { | |||
| length_code = -1; // Insufficient data to compute length | |||
| } | |||
| } else { | |||
| if (data.length > 5) | |||
| mask = data.slice(2, 6); | |||
| } | |||
| if (length_code > -1 && data.length >= data_start + length_code) { | |||
| var decoded = data.slice(data_start, data_start + length_code).map(function(byte, index) { | |||
| return byte ^ mask[index % 4]; | |||
| }); | |||
| if (op == 1) { | |||
| decoded = ary2utf8(decoded); | |||
| } | |||
| data = data.slice(data_start + length_code); | |||
| if (fin && op > 0) { | |||
| // Unfragmented message. | |||
| if (!t.onFrame_(op, decoded)) | |||
| return; | |||
| } else { | |||
| // Fragmented message. | |||
| fragmentedOp = fragmentedOp || op; | |||
| fragmentedMessages.push(decoded); | |||
| if (fin) { | |||
| var joinMessage = null; | |||
| if (op == 1) { | |||
| joinMessage = fragmentedMessagess.join(''); | |||
| } else { | |||
| joinMessage = fragmentedMessages.reduce(function(pre, cur) { | |||
| return Array.prototype.push.apply(pre, cur); | |||
| }, []); | |||
| } | |||
| if (!t.onFrame_(fragmentedOp, joinMessage)) | |||
| return; | |||
| fragmentedOp = 0; | |||
| fragmentedMessages = []; | |||
| } | |||
| } | |||
| } else { | |||
| break; // Insufficient data, wait for more. | |||
| } | |||
| } | |||
| return t.pSocket_.read().then(onDataRead); | |||
| }; | |||
| this.pSocket_.read().then(function(data) { | |||
| return onDataRead(data); | |||
| }).catch(function(e) { | |||
| t.close_(); | |||
| }); | |||
| }, | |||
| onFrame_: function(op, data) { | |||
| if (op == 1 || op == 2) { | |||
| if (typeof data == 'string' || data instanceof String) { | |||
| // Don't do anything. | |||
| } else if (Array.isArray(data)) { | |||
| data = new Uint8Array(data).buffer; | |||
| } else if (data instanceof ArrayBuffer) { | |||
| // Don't do anything. | |||
| } else { | |||
| data = data.buffer; | |||
| } | |||
| this.dispatchEvent('message', {'data': data}); | |||
| } else if (op == 8) { | |||
| // A close message must be confirmed before the websocket is closed. | |||
| if (this.readyState === 1) { | |||
| this.sendFrame_(8); | |||
| this.readyState = 2; | |||
| } else { | |||
| this.close_(); | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| }, | |||
| sendFrame_: function(op, data) { | |||
| var t = this; | |||
| var WebsocketFrameData = function(op, data) { | |||
| var ary = data; | |||
| if (typeof data == 'string' || data instanceof String) { | |||
| ary = utf82ary(data); | |||
| } | |||
| if (Array.isArray(ary)) { | |||
| ary = new Uint8Array(ary); | |||
| } | |||
| if (ary instanceof ArrayBuffer) { | |||
| ary = new Uint8Array(ary); | |||
| } | |||
| ary = new Uint8Array(ary.buffer); | |||
| var length = ary.length; | |||
| if (ary.length > 65535) | |||
| length += 10; | |||
| else if (ary.length > 125) | |||
| length += 4; | |||
| else | |||
| length += 2; | |||
| var lengthBytes = 0; | |||
| var buffer = new ArrayBuffer(length); | |||
| var bv = new Uint8Array(buffer); | |||
| bv[0] = 128 | (op & 15); // Fin and type text. | |||
| bv[1] = ary.length > 65535 ? 127 : | |||
| (ary.length > 125 ? 126 : ary.length); | |||
| if (ary.length > 65535) | |||
| lengthBytes = 8; | |||
| else if (ary.length > 125) | |||
| lengthBytes = 2; | |||
| var len = ary.length; | |||
| for (var i = lengthBytes - 1; i >= 0; i--) { | |||
| bv[2 + i] = len & 255; | |||
| len = len >> 8; | |||
| } | |||
| var dataStart = lengthBytes + 2; | |||
| for (var i = 0; i < ary.length; i++) { | |||
| bv[dataStart + i] = ary[i]; | |||
| } | |||
| return buffer; | |||
| } | |||
| var array = WebsocketFrameData(op, data || ''); | |||
| this.pSocket_.write(array).then(function(bytesWritten) { | |||
| if (bytesWritten !== array.byteLength) | |||
| throw new Error('insufficient write'); | |||
| }).catch(function(e) { | |||
| t.close_(); | |||
| }); | |||
| }, | |||
| close_: function() { | |||
| if (this.readyState !== 3) { | |||
| this.pSocket_.close(); | |||
| this.readyState = 3; | |||
| this.dispatchEvent('close'); | |||
| } | |||
| } | |||
| }; | |||
| return { | |||
| 'Server': HttpServer, | |||
| 'WebSocketServer': WebSocketServer, | |||
| }; | |||
| }(); | |||
| @ -0,0 +1,27 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <script src="event.js"></script> | |||
| <script src="sha1.js"></script> | |||
| <script src="http.js"></script> | |||
| <script src="main.js"></script> | |||
| <link rel="stylesheet" href="style.css"> | |||
| </head> | |||
| <body> | |||
| <div class="msg"> | |||
| <div id="buffer"> </div> | |||
| <div id="msg_end"></div> | |||
| </div> | |||
| <div class="buttons"> | |||
| <input type="checkbox" id="show-log"> Debug | |||
| <button id="enroll">Enroll</button> | |||
| <button id="down">Down</button> | |||
| <button id="match">Match</button> | |||
| <button id="refresh">Refesh Device</button> | |||
| <button id="export">Export</button> | |||
| <button id="import">Import&Down</button> | |||
| <input type="file" id="import-file" style="display:none"/> | |||
| </div> | |||
| </body> | |||
| </html> | |||
| @ -0,0 +1,883 @@ | |||
| const serial = chrome.serial; | |||
| var showDebugLog = false; | |||
| var SerialConnection = function() { | |||
| this.connectionId = -1; | |||
| this.boundOnReceive = this.onReceive.bind(this); | |||
| this.boundOnReceiveError = this.onReceiveError.bind(this); | |||
| this.onConnect = new MyEvent(); | |||
| this.onReceive = new MyEvent(); | |||
| this.onError = new MyEvent(); | |||
| this.recvBuffer = new Uint8Array(8*1024); | |||
| this.recvView = new DataView(this.recvBuffer.buffer); | |||
| this.recvCursor = 0; | |||
| this.bitrate = 57600; | |||
| }; | |||
| SerialConnection.prototype.onConnectComplete = function(connectionInfo) { | |||
| if (!connectionInfo) { | |||
| if (chrome.runtime.lastError != undefined) { | |||
| logln('chrome.serial.connect error: ' + chrome.runtime.lastError.message); | |||
| } | |||
| return; | |||
| } | |||
| this.connectionId = connectionInfo.connectionId; | |||
| chrome.serial.onReceive.addListener(this.boundOnReceive); | |||
| chrome.serial.onReceiveError.addListener(this.boundOnReceiveError); | |||
| this.onConnect.dispatch(); | |||
| }; | |||
| SerialConnection.prototype.onReceive = function(receiveInfo) { | |||
| if (receiveInfo.connectionId !== this.connectionId) { | |||
| return; | |||
| } | |||
| this.recvBuffer.set(new Uint8Array(receiveInfo.data), this.recvCursor); | |||
| this.recvCursor += receiveInfo.data.byteLength; | |||
| //console.log(buf2hex(receiveInfo.data)); | |||
| if (this.recvCursor < 6) { | |||
| return; | |||
| } | |||
| this._dispathReceiveData(); | |||
| }; | |||
| SerialConnection.prototype.clearRecvData = function() { | |||
| this.recvCursor = 0; | |||
| this.recvBuffer.fill(0); | |||
| } | |||
| SerialConnection.prototype._dispathReceiveData = function() { | |||
| var dLen = this.recvView.getUint16(7); | |||
| if (this.recvCursor < dLen + 9) { | |||
| return; | |||
| } | |||
| var dataBuffer = new Uint8Array(dLen + 9); | |||
| dataBuffer.set(this.recvBuffer.subarray(0, dLen + 9)); | |||
| if (showDebugLog) { | |||
| logln("recv: " + buf2hex(dataBuffer.buffer)); | |||
| } | |||
| var realCrc = calcCRC(dataBuffer, 6, dLen + 7); | |||
| var crc = dataBuffer[dLen+7] * 256 + dataBuffer[dLen+8]; | |||
| if (crc != realCrc) { | |||
| logln("invalid crc " + crc + " ,real= " + realCrc); | |||
| }else { | |||
| var packet = new Packet().fromDataBuffer(dataBuffer); | |||
| this.onReceive.dispatch(packet); | |||
| } | |||
| if (this.recvCursor > dLen + 9) { | |||
| dataBuffer = new Uint8Array(this.recvCursor - dLen - 6); | |||
| dataBuffer.set(this.recvBuffer.subarray(dLen + 9, this.recvCursor)); | |||
| this.recvBuffer.fill(0); | |||
| this.recvBuffer.set(dataBuffer); | |||
| this.recvCursor -= dLen + 9; | |||
| this._dispathReceiveData(); | |||
| }else { | |||
| this.recvCursor = 0; | |||
| this.recvBuffer.fill(0); | |||
| } | |||
| } | |||
| SerialConnection.prototype.onReceiveError = function(errorInfo) { | |||
| if (errorInfo.connectionId === this.connectionId) { | |||
| this.onError.dispatch(errorInfo.error); | |||
| } | |||
| }; | |||
| SerialConnection.prototype.update = function(conf, cb) { | |||
| if (!this.connectionId) {return;} | |||
| serial.update(this.connectionId, conf, cb); | |||
| } | |||
| SerialConnection.prototype.connect = function(path, bitrate=57600) { | |||
| this.clearRecvData(); | |||
| this.bitrate = bitrate; | |||
| serial.connect(path, { bitrate: this.bitrate },this.onConnectComplete.bind(this)) | |||
| }; | |||
| SerialConnection.prototype.send = function(packet) { | |||
| if (this.connectionId < 0) { | |||
| throw 'Invalid connection'; | |||
| } | |||
| var data = packet.getDataBuffer(); | |||
| if (showDebugLog) { | |||
| logln("send: " + buf2hex(data)); | |||
| } | |||
| serial.send(this.connectionId, data, function() {}); | |||
| }; | |||
| SerialConnection.prototype.disconnect = function() { | |||
| if (this.connectionId < 0) { | |||
| throw 'Invalid connection'; | |||
| } | |||
| serial.disconnect(this.connectionId, function() {}); | |||
| }; | |||
| //////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////// | |||
| function logln(msg) { | |||
| var buffer = document.querySelector('#buffer'); | |||
| buffer.innerHTML += msg + '<br/>'; | |||
| var msgEnd = document.querySelector('#msg_end'); | |||
| msgEnd.scrollIntoView(); | |||
| } | |||
| function log(msg) { | |||
| var buffer = document.querySelector('#buffer'); | |||
| buffer.innerHTML += msg; | |||
| var msgEnd = document.querySelector('#msg_end'); | |||
| msgEnd.scrollIntoView(); | |||
| } | |||
| function buf2hex(buffer) { | |||
| return '0x' + Array.prototype.map.call(new Uint8Array(buffer), x => ('0x00' + x.toString(16)).slice(-2)).join(' 0x'); | |||
| } | |||
| function calcCRC(buffer, start, end) { | |||
| var crc = 0; | |||
| for (var i = start; i < end; i++) { | |||
| crc += buffer[i] & 0xff; | |||
| } | |||
| return crc & 0xffff; | |||
| } | |||
| const PacketTypeCmd = 0x01; | |||
| const PacketTypeData = 0x02; | |||
| const PacketTypeDataEnd = 0x08; | |||
| const PacketTypeCmdResp = 0x07; | |||
| const PacketTypeDataResp = 0x09; | |||
| var Packet = function(data=new Uint8Array([0x35]), dataLen=1, type=PacketTypeCmd) { | |||
| this.dataBuffer = new Uint8Array(512); | |||
| this.dataBuffer.set([0xEF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]); | |||
| this.type = type; | |||
| this.dataLen = dataLen; | |||
| this.data = data; | |||
| this.result = 0; | |||
| } | |||
| Packet.prototype.setCmd = function(cmd) { | |||
| this.type = PacketTypeCmd; | |||
| this.data = Uint8Array.of(cmd); | |||
| this.dataLen = 1; | |||
| } | |||
| Packet.prototype.getDataBuffer = function () { | |||
| var dataView = new DataView(this.dataBuffer.buffer); | |||
| dataView.setUint8(6, this.type); | |||
| var len = this.dataLen + 2; | |||
| dataView.setUint16(7, len); | |||
| this.dataBuffer.set(this.data, 9); | |||
| var crc = calcCRC(this.dataBuffer, 6, this.dataLen + 9); | |||
| dataView.setUint16(9 + this.dataLen, crc); | |||
| return new Uint8Array(this.dataBuffer.buffer, 0, this.dataLen + 11); | |||
| } | |||
| Packet.prototype.fromDataBuffer = function(buffer) { | |||
| this.dataBuffer.set(buffer); | |||
| var dataView = new DataView(this.dataBuffer.buffer); | |||
| this.type = dataView.getUint8(6); | |||
| var len = dataView.getUint16(7); | |||
| this.dataLen = len - 2; | |||
| this.data = new Uint8Array(buffer.buffer, 9, this.dataLen); | |||
| if (this.type == PacketTypeCmdResp) { | |||
| this.result = this.data[0]; | |||
| } | |||
| return this; | |||
| } | |||
| //////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////// | |||
| var curDevice = null; | |||
| function tryHandshake(device) { | |||
| return new Promise((resolve, reject) =>{ | |||
| logln("try handshake with " + device.path); | |||
| var connection = new SerialConnection(); | |||
| var isValidDevice = 0; | |||
| device.bitrate = 57600; | |||
| connection.onConnect.addListener(function() { | |||
| logln("device " + device.path + " connected"); | |||
| connection.send(new Packet()); | |||
| }); | |||
| connection.onReceive.addListener(function(packet) { | |||
| if (packet.type == PacketTypeCmdResp && packet.result == 0) { | |||
| isValidDevice = 1; | |||
| connection.disconnect(); | |||
| resolve(1) | |||
| } | |||
| }); | |||
| connection.connect(device.path, device.bitrate); | |||
| setTimeout(() => { | |||
| if (isValidDevice) { | |||
| }else { | |||
| device.bitrate = 115200; | |||
| connection.update({ | |||
| bitrate: device.bitrate | |||
| }, (result) => { | |||
| if (result) { | |||
| connection.send(new Packet()); | |||
| setTimeout(() => { | |||
| connection.disconnect(); | |||
| if (!isValidDevice) { | |||
| resolve(0); | |||
| } | |||
| }, 500); | |||
| }else { | |||
| connection.disconnect(); | |||
| resolve(0); | |||
| } | |||
| }) | |||
| } | |||
| }, 500); | |||
| }) | |||
| } | |||
| async function checkDevices(devices) { | |||
| for (var device of devices) { | |||
| logln("found device, path = " + device.path); | |||
| var res = await tryHandshake(device); | |||
| if (res) { | |||
| curDevice = device; | |||
| logln("found valid device " + device.path); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| //////////////////////////////////////////////////////// | |||
| var EnrollSchedule = function(device, enrollCount=6, callback=((err=null, step=0, state=0, data=null)=>{}), timeout = 60 * 1000) { | |||
| this.step = 0; | |||
| this.enrollCount = enrollCount; | |||
| this.callback = callback; | |||
| this.device = device; | |||
| this.connection = null; | |||
| this.timeout = timeout; | |||
| this.timeoutFlag = 0; | |||
| this.responseTime = 1000; | |||
| this.responseTimeout = 0; | |||
| this.responseCallback = null; | |||
| this.canceled = false; | |||
| } | |||
| const STATE_WAIT_FINGER_DOWN = 1; | |||
| const STATE_WAIT_FINGER_LEAVE = 2; | |||
| const STATE_FINGER_DOWN = 3; | |||
| const STATE_FINGER_LEAVE = 4; | |||
| const STATE_EXTRACT_TEMPLATE = 5; | |||
| const STATE_DOWNLOAD_TEMPLATE = 6; | |||
| const STATE_EXTRACT_TEMPLATE_FAIL = 100; | |||
| const STATE_EXTRACT_TEMPLATE_DIRTY = 101; | |||
| const STATE_EXTRACT_TEMPLATE_POINTS_FEW = 102; | |||
| const STATE_EXTRACT_TEMPLATE_MERGE_FAIL = 103; | |||
| const ErrorReceiveDataErr = 1; | |||
| const ErrorEnrollTimeout = 2; | |||
| const ErrorDeviceNotFound = 3; | |||
| const ErrorEmptyFail = 4; | |||
| function printError(err) { | |||
| logln( "enroll err with code: " + err); | |||
| } | |||
| EnrollSchedule.prototype.start = async function() { | |||
| this.step = 0; | |||
| this.canceled = false; | |||
| try { | |||
| await this._connect(); | |||
| if(this.canceled) { return this._disConnect(); } | |||
| var ret = await this._sendAndWaitRecvCmd(0x0d); | |||
| if (ret == 0x11) { | |||
| throw ErrorEmptyFail; | |||
| } | |||
| while(this.step < this.enrollCount) { | |||
| this.step += 1; | |||
| ret = await this._enrollOne(this.step); | |||
| if (ret) { | |||
| let stateErr = STATE_EXTRACT_TEMPLATE_FAIL; | |||
| if(ret == 0x06) { | |||
| stateErr = STATE_EXTRACT_TEMPLATE_DIRTY; | |||
| }else if (ret == 0x07) { | |||
| stateErr = STATE_EXTRACT_TEMPLATE_POINTS_FEW; | |||
| }else if (ret == 0x0a) { | |||
| stateErr = STATE_EXTRACT_TEMPLATE_MERGE_FAIL; | |||
| } | |||
| this.callback(null, this.step, stateErr, null); | |||
| this.step -= 1; | |||
| } | |||
| if(this.canceled) { return this._disConnect(); } | |||
| } | |||
| logln("receive tempalte"); | |||
| this.callback(null, this.step, STATE_DOWNLOAD_TEMPLATE, null); | |||
| var ret = await this._mergeTemplate(); | |||
| if(this.canceled) { return this._disConnect(); } | |||
| var recvBuffer = await this._receiveTemplate(); | |||
| if(this.canceled) { return this._disConnect(); } | |||
| this.callback(null, this.step, 0, recvBuffer); | |||
| }catch(e) { | |||
| this.callback(e, this.step, 0, null); | |||
| } | |||
| this._disConnect(); | |||
| } | |||
| EnrollSchedule.prototype._mergeTemplate = function() { | |||
| return this._sendAndWaitRecvCmd(0x05); | |||
| } | |||
| EnrollSchedule.prototype._receiveTemplate = function() { | |||
| return new Promise((resolve, reject) => { | |||
| var that = this; | |||
| var data = Uint8Array.of(0x08, 0x01); | |||
| var packet = new Packet(data, 2); | |||
| var recvDataLen = 4 * 1024 * 1024; | |||
| var recvData = new Uint8Array(recvDataLen); | |||
| var recvDataCursor = 0; | |||
| var resetTimeoutCheck = function() { | |||
| if (that.responseTimeout) { | |||
| clearTimeout(that.responseTimeout); | |||
| } | |||
| that.responseTimeout = setTimeout(() => { | |||
| var dummy = new Packet(); | |||
| that.connection.send(dummy); | |||
| that.responseTimeout = setTimeout(() => { | |||
| reject(ErrorReceiveDataErr); | |||
| }, that.responseTime); | |||
| }, that.responseTime); | |||
| } | |||
| this.responseCallback = (packet) => { | |||
| resetTimeoutCheck(); | |||
| if (packet.type == PacketTypeCmdResp) { | |||
| }else if (packet.type == PacketTypeData || packet.type == PacketTypeDataEnd) { | |||
| if (recvDataCursor + packet.dataLen < recvDataLen) { | |||
| recvData.set(packet.data, recvDataCursor); | |||
| recvDataCursor += packet.dataLen; | |||
| }else { | |||
| if (that.responseTimeout) { | |||
| clearTimeout(that.responseTimeout); | |||
| } | |||
| reject("recv buffer full"); | |||
| } | |||
| } | |||
| if (packet.type == PacketTypeDataEnd) { | |||
| if (that.responseTimeout) { | |||
| clearTimeout(that.responseTimeout); | |||
| } | |||
| resolve(recvData.slice(0, recvDataCursor)); | |||
| } | |||
| } | |||
| this.connection.send(packet); | |||
| resetTimeoutCheck(); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype._sendAndWaitRecvCmd = function(cmd) { | |||
| return new Promise((resolve, reject) =>{ | |||
| var packet = new Packet(); | |||
| packet.setCmd(cmd); | |||
| this._sendAndWaitRecv(packet).then(packet => { | |||
| if (packet.type == PacketTypeCmdResp) { | |||
| resolve(packet.result); | |||
| } | |||
| }).catch(err => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype._enrollOne = async function(step) { | |||
| var down = false; | |||
| var leave = false; | |||
| var isTimeout = false; | |||
| this.timeoutFlag = setTimeout(() => { | |||
| if (!down) { | |||
| isTimeout = true; | |||
| } | |||
| }, this.timeout); | |||
| //wait finger leave | |||
| logln("wait leave") | |||
| this.callback(null, this.step, STATE_WAIT_FINGER_LEAVE, null); | |||
| while (leave == false && !isTimeout) { | |||
| if(this.canceled) { return; } | |||
| leave = !(await this.captureOne()); | |||
| } | |||
| this.callback(null, this.step, STATE_FINGER_LEAVE, null); | |||
| if (isTimeout) { | |||
| throw ErrorEnrollTimeout; | |||
| } | |||
| this.callback(null, this.step, STATE_WAIT_FINGER_DOWN, null); | |||
| //wait finger down | |||
| logln("wait down") | |||
| while (!down && !isTimeout) { | |||
| if(this.canceled) { return; } | |||
| down = await this.captureOne(); | |||
| } | |||
| if (isTimeout) { | |||
| throw ErrorEnrollTimeout; | |||
| } | |||
| this.callback(null, this.step, STATE_FINGER_DOWN, null); | |||
| logln("finger down step = " + step); | |||
| if(this.canceled) { return; } | |||
| this.callback(null, this.step, STATE_EXTRACT_TEMPLATE, null); | |||
| var ret = await this.extractOne(step); | |||
| if (ret != 0) { | |||
| logln("extract tempate err " + ret); | |||
| return ret; | |||
| } | |||
| return 0; | |||
| } | |||
| EnrollSchedule.prototype.extractOne = function(step) { | |||
| return new Promise((resolve, reject) =>{ | |||
| var data = Uint8Array.of(0x02, step); | |||
| var packet = new Packet(data, 2); | |||
| this._sendAndWaitRecv(packet).then(packet => { | |||
| if (packet.type == PacketTypeCmdResp) { | |||
| resolve(packet.result); | |||
| } | |||
| }).catch(err => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype.captureOne = function() { | |||
| return new Promise((resolve, reject) => { | |||
| var packet = new Packet(); | |||
| packet.setCmd(0x01); | |||
| this._sendAndWaitRecv(packet).then(packet => { | |||
| if (packet.type == PacketTypeCmdResp) { | |||
| resolve(packet.result == 0); | |||
| } | |||
| }).catch(err => { | |||
| reject(err); | |||
| }); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype._sendAndWaitRecv = function(packet) { | |||
| return new Promise((resolve, reject) => { | |||
| var that = this; | |||
| this.responseCallback = (packet) => { | |||
| if (packet.type == PacketTypeCmdResp) { | |||
| if (that.responseTimeout) { | |||
| clearTimeout(that.responseTimeout); | |||
| that.responseTimeout = 0; | |||
| } | |||
| if (packet.result == 0x01) { | |||
| reject(ErrorReceiveDataErr); | |||
| }else { | |||
| resolve(packet); | |||
| } | |||
| } | |||
| } | |||
| this.connection.send(packet); | |||
| this.responseTimeout = setTimeout(() => { | |||
| that.connection.send(packet); | |||
| this.responseTimeout = setTimeout(() => { | |||
| reject(ErrorReceiveDataErr); | |||
| }, this.responseTime); | |||
| }, this.responseTime); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype._resolvePacket = function(packet) { | |||
| if (this.responseCallback) { | |||
| this.responseCallback(packet); | |||
| } | |||
| } | |||
| EnrollSchedule.prototype._connect = function() { | |||
| return new Promise((resolve, reject) => { | |||
| var that = this; | |||
| this.connection = new SerialConnection(); | |||
| this.connection.onConnect.addListener(function() { | |||
| logln("device " + that.device.path + " connected"); | |||
| resolve(); | |||
| }); | |||
| this.connection.onReceive.addListener(function(packet) { | |||
| that._resolvePacket(packet); | |||
| }); | |||
| this.connection.connect(this.device.path, this.device.bitrate); | |||
| setTimeout(() => { | |||
| reject("connect timeout"); | |||
| }, 500); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype._disConnect = function() { | |||
| if (this.connection) { | |||
| this.connection.disconnect(); | |||
| this.connection = null; | |||
| } | |||
| if (this.responseTimeout) { | |||
| clearTimeout(this.responseTimeout); | |||
| this.responseTimeout = 0; | |||
| } | |||
| if (this.timeoutFlag) { | |||
| clearTimeout(this.timeoutFlag); | |||
| this.timeoutFlag = 0; | |||
| } | |||
| } | |||
| EnrollSchedule.prototype.stop = function() { | |||
| this.canceled = true; | |||
| } | |||
| var enrollSchedule = null; | |||
| var tempData = null; | |||
| function startEnroll(enrollCount=6, timeout=10*1000, cb) { | |||
| if (!curDevice) { | |||
| logln("device not found"); | |||
| cb.call(null, { | |||
| err: ErrorDeviceNotFound | |||
| }, 0, null); | |||
| return; | |||
| } | |||
| enrollSchedule = new EnrollSchedule(curDevice, enrollCount, (err, step, state, data) => { | |||
| if (err) { | |||
| printError(status.err); | |||
| } | |||
| if(cb) { | |||
| cb.call(null, { | |||
| err: err, | |||
| state: state, | |||
| step: step, | |||
| data: data | |||
| }); | |||
| } | |||
| if (data) { | |||
| tempData = data; | |||
| } | |||
| }, timeout); | |||
| logln("start enroll"); | |||
| enrollSchedule.start(); | |||
| } | |||
| function stopEnroll() { | |||
| if (enrollSchedule) { | |||
| enrollSchedule.stop(); | |||
| enrollSchedule = null; | |||
| } | |||
| } | |||
| //////////////////////////////////////////////////////// | |||
| //test | |||
| EnrollSchedule.prototype.sleep = function(time) { | |||
| return new Promise((resolve, reject) => { | |||
| setTimeout(() => { | |||
| resolve(); | |||
| }, time); | |||
| }); | |||
| } | |||
| EnrollSchedule.prototype.downloadTemplate = async function(data) { | |||
| var cmdData = Uint8Array.of(0x09, 0x02); | |||
| var packet = new Packet(cmdData, 2); | |||
| this.connection.send(packet); | |||
| await this.sleep(1000); | |||
| var dataLength = data.length; | |||
| var packetLen = 256; | |||
| var sendCursor = 0; | |||
| var sendLen = 0; | |||
| var sendType = PacketTypeData; | |||
| logln("downloading"); | |||
| while (sendCursor < dataLength) { | |||
| sendLen = (dataLength-sendCursor > packetLen) ? packetLen : (dataLength-sendCursor); | |||
| sendType = (dataLength-sendCursor > packetLen) ? PacketTypeData : PacketTypeDataEnd; | |||
| var sendData = new Uint8Array(data.buffer, sendCursor, sendLen); | |||
| var dataPack = new Packet(sendData, sendLen, sendType); | |||
| this.connection.send(dataPack); | |||
| console.log("send "+ sendLen) | |||
| log('.'); | |||
| sendCursor += sendLen; | |||
| await this.sleep(100); | |||
| } | |||
| cmdData = Uint8Array.of(0x06, 0x02, 0x00, 0x03); | |||
| packet = new Packet(cmdData, 4); | |||
| this.connection.send(packet); | |||
| logln('.'); | |||
| await this.sleep(500); | |||
| } | |||
| async function downloadTemplate() { | |||
| if (!curDevice) { | |||
| var err = "device not found"; | |||
| logln(err); | |||
| return; | |||
| } | |||
| if (!tempData) { | |||
| var err = "please enroll"; | |||
| logln(err); | |||
| return; | |||
| } | |||
| var enroll = new EnrollSchedule(curDevice); | |||
| try { | |||
| await enroll._connect(); | |||
| await enroll.downloadTemplate(tempData); | |||
| logln("download complete"); | |||
| }catch(err) { | |||
| logln("download err: " + err); | |||
| } | |||
| await enroll._disConnect(); | |||
| } | |||
| async function matchTemplate() { | |||
| if (!curDevice) { | |||
| var err = "device not found"; | |||
| logln(err); | |||
| return; | |||
| } | |||
| var enroll = new EnrollSchedule(curDevice); | |||
| try { | |||
| await enroll._connect(); | |||
| await enroll._enrollOne(1); | |||
| var packData = Uint8Array.of(0x04, 0x01, 0x00, 0x03, 0x00, 0x03); | |||
| var packet = new Packet(packData, 6); | |||
| await new Promise((resolve, reject) =>{ | |||
| enroll._sendAndWaitRecv(packet).then(packet => { | |||
| if (packet.type == PacketTypeCmdResp) { | |||
| if(packet.result) { | |||
| logln("match fail"); | |||
| }else { | |||
| logln("match success score " + (packet.data[3] * 256 + packet.data[4])); | |||
| } | |||
| resolve(); | |||
| } | |||
| }).catch(err => { | |||
| reject("match err: " + err); | |||
| }); | |||
| }) | |||
| }catch(err) { | |||
| logln("download err: " + err); | |||
| } | |||
| await enroll._disConnect(); | |||
| } | |||
| //////////////////////////////////////////////////////// | |||
| //////////////////////////////////////////////////////// | |||
| function disableAll() { | |||
| document.querySelector("#enroll").disabled = true; | |||
| document.querySelector("#down").disabled = true; | |||
| document.querySelector("#match").disabled = true; | |||
| document.querySelector("#refresh").disabled = true; | |||
| document.querySelector("#export").disabled = true; | |||
| document.querySelector("#import").disabled = true; | |||
| } | |||
| function enableAll() { | |||
| document.querySelector("#enroll").disabled = false; | |||
| document.querySelector("#down").disabled = false; | |||
| document.querySelector("#match").disabled = false; | |||
| document.querySelector("#refresh").disabled = false; | |||
| document.querySelector("#export").disabled = false; | |||
| document.querySelector("#import").disabled = false; | |||
| } | |||
| var server = null; | |||
| onload = function() { | |||
| document.querySelector('#show-log').checked = showDebugLog; | |||
| document.querySelector('#show-log').onchange = function(e) { | |||
| showDebugLog = e.target.checked; | |||
| } | |||
| document.querySelector('#enroll').onclick = function(e) { | |||
| if (e.currentTarget.innerText == "Enroll") { | |||
| e.currentTarget.innerText = "Stop"; | |||
| startEnroll(6, 10* 1000, (status) => { | |||
| console.log(status.step); | |||
| if (status.err) { | |||
| console.log(status.err); | |||
| } | |||
| if (status.err || status.data) { | |||
| document.querySelector('#enroll').innerText = "Enroll"; | |||
| } | |||
| if (status.data) { | |||
| // console.log(data); | |||
| // console.log(buf2hex(data.buffer)); | |||
| } | |||
| }); | |||
| }else { | |||
| e.currentTarget.innerText = "Enroll"; | |||
| stopEnroll(); | |||
| } | |||
| } | |||
| document.querySelector('.msg').style="height:" + (window.innerHeight - 70) + "px"; | |||
| chrome.serial.getDevices((devices) => { | |||
| checkDevices(devices); | |||
| }); | |||
| document.querySelector("#down").onclick = async function(e) { | |||
| disableAll(); | |||
| try { | |||
| await downloadTemplate(); | |||
| }catch(err) { | |||
| logln("download err: " + err); | |||
| } | |||
| enableAll(); | |||
| } | |||
| document.querySelector("#match").onclick = async function(e) { | |||
| disableAll(); | |||
| try { | |||
| await matchTemplate(); | |||
| }catch(err) { | |||
| logln("match err: " + err); | |||
| } | |||
| enableAll(); | |||
| } | |||
| document.querySelector("#refresh").onclick = function(e) { | |||
| disableAll(); | |||
| chrome.serial.getDevices(async function(devices) { | |||
| try { | |||
| await checkDevices(devices); | |||
| }catch (e) { | |||
| console.log(e) | |||
| } | |||
| enableAll(); | |||
| }); | |||
| } | |||
| document.querySelector("#export").onclick = function(e) { | |||
| if (!tempData) { | |||
| logln("please enroll"); | |||
| return; | |||
| } | |||
| let reader = new FileReader(); | |||
| reader.onload = function(eve) { | |||
| if (eve.target.readyState == FileReader.DONE) | |||
| { | |||
| let a = document.createElement('a'); | |||
| a.download = "mafp_template.bin"; | |||
| a.href = this.result; | |||
| a.click(); | |||
| } | |||
| } | |||
| reader.readAsDataURL(new Blob([tempData])); | |||
| } | |||
| document.querySelector("#import").onclick = function(e) { | |||
| let fileInput = document.querySelector("#import-file"); | |||
| fileInput.value = ""; | |||
| fileInput.click(); | |||
| } | |||
| document.querySelector("#import-file").onchange = function(e) { | |||
| let files = e.target.files; | |||
| if (files && files.length) { | |||
| let reader = new FileReader(); | |||
| reader.onload = async function(ev) { | |||
| if (ev.total == 4096) { | |||
| tempData = new Uint8Array(this.result); | |||
| disableAll(); | |||
| try { | |||
| await downloadTemplate(); | |||
| }catch(err) { | |||
| logln("download err: " + err); | |||
| } | |||
| enableAll(); | |||
| }else { | |||
| logln("invalid file length " + ev.total); | |||
| } | |||
| } | |||
| reader.readAsArrayBuffer(files[0]); | |||
| } | |||
| } | |||
| const port = 9897; | |||
| if (http.Server && http.WebSocketServer) { | |||
| server = new http.Server(); | |||
| var wsServer = new http.WebSocketServer(server); | |||
| server.listen(port); | |||
| logln("ws socket server listen at " + port); | |||
| var connectedSocket = null; | |||
| wsServer.addEventListener('request', function(req) { | |||
| if (connectedSocket) { | |||
| req.reject(); | |||
| return; | |||
| } | |||
| console.log('Client connected'); | |||
| var socket = req.accept(); | |||
| connectedSocket = socket; | |||
| socket.addEventListener('message', function(e) { | |||
| var reqData = JSON.parse(e.data); | |||
| if (reqData.cmd == "enrollStart") { | |||
| startEnroll(reqData.config.enrollCount, | |||
| reqData.config.enrollTimeout, | |||
| (status) =>{ | |||
| var resp = { | |||
| err: status.err ? status.err : "", | |||
| state: status.state ? status.state : 0, | |||
| step: status.step | |||
| } | |||
| if (status.data) { | |||
| resp.data = Array.from(status.data); | |||
| } | |||
| connectedSocket.send(JSON.stringify(resp)); | |||
| }); | |||
| }else if (reqData.cmd == "enrollCancel") { | |||
| stopEnroll(); | |||
| } | |||
| }); | |||
| socket.addEventListener('close', function() { | |||
| connectedSocket = null; | |||
| stopEnroll(); | |||
| console.log('Client disconnected'); | |||
| if (chrome.runtime.lastError != undefined) { | |||
| console.log(chrome.runtime.lastError.message); | |||
| } | |||
| }); | |||
| return true; | |||
| }); | |||
| } | |||
| }; | |||
| onresize = function(e) { | |||
| document.querySelector('.msg').style="height:" + (e.currentTarget.innerHeight - 70) + "px"; | |||
| document.querySelector('#msg_end').scrollIntoView(); | |||
| } | |||
| @ -0,0 +1,30 @@ | |||
| { | |||
| "name": "MAFP Serial", | |||
| "version": "1.0", | |||
| "manifest_version": 2, | |||
| "minimum_chrome_version": "23", | |||
| "description": "microarray fingerprint serial control.", | |||
| "icons": | |||
| { | |||
| "48": "logo.png", | |||
| "128": "logo.png" | |||
| }, | |||
| "app": { | |||
| "background": { | |||
| "scripts": [ "background.js" ] | |||
| } | |||
| }, | |||
| "sockets": { | |||
| "tcp": { | |||
| "connect": "*" | |||
| }, | |||
| "tcpServer": { | |||
| "listen": "*" | |||
| } | |||
| }, | |||
| "permissions": [ | |||
| "serial" | |||
| ] | |||
| } | |||
| @ -0,0 +1,232 @@ | |||
| // Licensed under the Apache License, Version 2.0 (the "License"); | |||
| // you may not use this file except in compliance with the License. | |||
| // You may obtain a copy of the License at | |||
| // | |||
| // http://www.apache.org/licenses/LICENSE-2.0 | |||
| // | |||
| // Unless required by applicable law or agreed to in writing, software | |||
| // distributed under the License is distributed on an "AS IS" BASIS, | |||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
| // See the License for the specific language governing permissions and | |||
| // limitations under the License. | |||
| // Copyright 2005 Google Inc. All Rights Reserved. | |||
| /** | |||
| * @fileoverview SHA-1 cryptographic hash. | |||
| * Variable names follow the notation in FIPS PUB 180-3: | |||
| * http://csrc.nist.gov/publications/fips/fips180-3/fips180-3_final.pdf. | |||
| * | |||
| * Usage: | |||
| * var sha1 = new goog.crypt.sha1(); | |||
| * sha1.update(bytes); | |||
| * var hash = sha1.digest(); | |||
| * | |||
| */ | |||
| /** | |||
| * SHA-1 cryptographic hash constructor. | |||
| * | |||
| * The properties declared here are discussed in the above algorithm document. | |||
| * @constructor | |||
| */ | |||
| var Sha1 = function() { | |||
| /** | |||
| * Holds the previous values of accumulated variables a-e in the compress_ | |||
| * function. | |||
| * @type {Array.<number>} | |||
| * @private | |||
| */ | |||
| this.chain_ = []; | |||
| /** | |||
| * A buffer holding the partially computed hash result. | |||
| * @type {Array.<number>} | |||
| * @private | |||
| */ | |||
| this.buf_ = []; | |||
| /** | |||
| * An array of 80 bytes, each a part of the message to be hashed. Referred to | |||
| * as the message schedule in the docs. | |||
| * @type {Array.<number>} | |||
| * @private | |||
| */ | |||
| this.W_ = []; | |||
| /** | |||
| * Contains data needed to pad messages less than 64 bytes. | |||
| * @type {Array.<number>} | |||
| * @private | |||
| */ | |||
| this.pad_ = []; | |||
| this.pad_[0] = 128; | |||
| for (var i = 1; i < 64; ++i) { | |||
| this.pad_[i] = 0; | |||
| } | |||
| this.reset(); | |||
| }; | |||
| /** | |||
| * Resets the internal accumulator. | |||
| */ | |||
| Sha1.prototype.reset = function() { | |||
| this.chain_[0] = 0x67452301; | |||
| this.chain_[1] = 0xefcdab89; | |||
| this.chain_[2] = 0x98badcfe; | |||
| this.chain_[3] = 0x10325476; | |||
| this.chain_[4] = 0xc3d2e1f0; | |||
| this.inbuf_ = 0; | |||
| this.total_ = 0; | |||
| }; | |||
| /** | |||
| * Internal helper performing 32 bit left rotate. | |||
| * @return {number} w rotated left by r bits. | |||
| * @private | |||
| */ | |||
| Sha1.prototype.rotl_ = function(w, r) { | |||
| return ((w << r) | (w >>> (32 - r))) & 0xffffffff; | |||
| }; | |||
| /** | |||
| * Internal compress helper function. | |||
| * @param {Array} buf containing block to compress. | |||
| * @private | |||
| */ | |||
| Sha1.prototype.compress_ = function(buf) { | |||
| var W = this.W_; | |||
| // get 16 big endian words | |||
| for (var i = 0; i < 64; i += 4) { | |||
| var w = (buf[i] << 24) | | |||
| (buf[i + 1] << 16) | | |||
| (buf[i + 2] << 8) | | |||
| (buf[i + 3]); | |||
| W[i / 4] = w; | |||
| } | |||
| // expand to 80 words | |||
| for (var i = 16; i < 80; i++) { | |||
| W[i] = this.rotl_(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1); | |||
| } | |||
| var a = this.chain_[0]; | |||
| var b = this.chain_[1]; | |||
| var c = this.chain_[2]; | |||
| var d = this.chain_[3]; | |||
| var e = this.chain_[4]; | |||
| var f, k; | |||
| for (var i = 0; i < 80; i++) { | |||
| if (i < 40) { | |||
| if (i < 20) { | |||
| f = d ^ (b & (c ^ d)); | |||
| k = 0x5a827999; | |||
| } else { | |||
| f = b ^ c ^ d; | |||
| k = 0x6ed9eba1; | |||
| } | |||
| } else { | |||
| if (i < 60) { | |||
| f = (b & c) | (d & (b | c)); | |||
| k = 0x8f1bbcdc; | |||
| } else { | |||
| f = b ^ c ^ d; | |||
| k = 0xca62c1d6; | |||
| } | |||
| } | |||
| var t = (this.rotl_(a, 5) + f + e + k + W[i]) & 0xffffffff; | |||
| e = d; | |||
| d = c; | |||
| c = this.rotl_(b, 30); | |||
| b = a; | |||
| a = t; | |||
| } | |||
| this.chain_[0] = (this.chain_[0] + a) & 0xffffffff; | |||
| this.chain_[1] = (this.chain_[1] + b) & 0xffffffff; | |||
| this.chain_[2] = (this.chain_[2] + c) & 0xffffffff; | |||
| this.chain_[3] = (this.chain_[3] + d) & 0xffffffff; | |||
| this.chain_[4] = (this.chain_[4] + e) & 0xffffffff; | |||
| }; | |||
| /** | |||
| * Adds a byte array to internal accumulator. | |||
| * @param {Array.<number>} bytes to add to digest. | |||
| * @param {number} opt_length is # of bytes to compress. | |||
| */ | |||
| Sha1.prototype.update = function(bytes, opt_length) { | |||
| if (!opt_length) { | |||
| opt_length = bytes.length; | |||
| } | |||
| var n = 0; | |||
| // Optimize for 64 byte chunks at 64 byte boundaries. | |||
| if (this.inbuf_ == 0) { | |||
| while (n + 64 < opt_length) { | |||
| this.compress_(bytes.slice(n, n + 64)); | |||
| n += 64; | |||
| this.total_ += 64; | |||
| } | |||
| } | |||
| while (n < opt_length) { | |||
| this.buf_[this.inbuf_++] = bytes[n++]; | |||
| this.total_++; | |||
| if (this.inbuf_ == 64) { | |||
| this.inbuf_ = 0; | |||
| this.compress_(this.buf_); | |||
| // Pick up 64 byte chunks. | |||
| while (n + 64 < opt_length) { | |||
| this.compress_(bytes.slice(n, n + 64)); | |||
| n += 64; | |||
| this.total_ += 64; | |||
| } | |||
| } | |||
| } | |||
| }; | |||
| /** | |||
| * @return {Array} byte[20] containing finalized hash. | |||
| */ | |||
| Sha1.prototype.digest = function() { | |||
| var digest = []; | |||
| var totalBits = this.total_ * 8; | |||
| // Add pad 0x80 0x00*. | |||
| if (this.inbuf_ < 56) { | |||
| this.update(this.pad_, 56 - this.inbuf_); | |||
| } else { | |||
| this.update(this.pad_, 64 - (this.inbuf_ - 56)); | |||
| } | |||
| // Add # bits. | |||
| for (var i = 63; i >= 56; i--) { | |||
| this.buf_[i] = totalBits & 255; | |||
| totalBits >>>= 8; | |||
| } | |||
| this.compress_(this.buf_); | |||
| var n = 0; | |||
| for (var i = 0; i < 5; i++) { | |||
| for (var j = 24; j >= 0; j -= 8) { | |||
| digest[n++] = (this.chain_[i] >> j) & 255; | |||
| } | |||
| } | |||
| return digest; | |||
| }; | |||
| @ -0,0 +1,32 @@ | |||
| body { | |||
| font-family: "Helvetica Neue", Helvetica, sans-serif; | |||
| font-size: 13px; | |||
| width: 100%; | |||
| height: 100%;; | |||
| margin: 0; | |||
| } | |||
| html { | |||
| width: 100%; | |||
| height: 100%;; | |||
| } | |||
| #buffer { | |||
| line-height: 20px; | |||
| } | |||
| .msg { | |||
| overflow: auto; | |||
| padding: 20px; | |||
| } | |||
| .buttons { | |||
| position: absolute; | |||
| bottom: 0; | |||
| display: flex; | |||
| align-items: center; | |||
| justify-content: flex-start; | |||
| height: 50px; | |||
| } | |||
| button { | |||
| margin-left: 20px; | |||
| } | |||
| @ -0,0 +1,2 @@ | |||
| <br /> | |||
| <b>Warning</b>: readfile(): Filename cannot be empty in <b>/www/wwwroot/51tool.com/ico/download.php</b> on line <b>17</b><br /> | |||
| @ -0,0 +1,3 @@ | |||
| { | |||
| "tenantId": "200,300,400" | |||
| } | |||