@ -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" | |||
} |