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