diff --git a/.babelrc b/.babelrc
new file mode 100644
index 0000000..557bb55
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1,22 @@
+{
+ "presets": [
+ ["env",
+ {
+ "targets": {
+ "browsers": "last 2 versions, > 1%, ie >= 6, Android >= 4, iOS >= 6, and_uc > 9",
+ "node": "0.10"
+ },
+ "modules": false,
+ "loose": false
+ }],
+ "stage-2"
+ ],
+ "plugins": [
+ ["transform-runtime", {
+ "helpers": false,
+ "polyfill": false,
+ "regenerator": false,
+ "moduleName": "babel-runtime"
+ }]
+ ]
+}
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..25b6d64
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[{*.js,*.css,*.html}]
+indent_style = space
+indent_size = 4
+end_of_line = lf
+charset = utf-8
+insert_final_newline = true
+
+[{package.json,.*rc,*.yml}]
+indent_style = space
+indent_size = 2
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..30d74d2
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+test
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..a7b704a
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,45 @@
+module.exports = {
+ root: true,
+
+ parserOptions: {
+ sourceType: 'module',
+ },
+
+ env: {
+ browser: true,
+ es6: true,
+ node: true,
+ },
+
+ extends: ['airbnb-base'],
+
+ rules: {
+ 'no-shadow': [
+ 'error',
+ {
+ allow: ['state'],
+ },
+ ],
+ // 'import/extensions': 'off',
+ 'import/extensions': [
+ 'error',
+ 'always',
+ {
+ js: 'never',
+ },
+ ],
+ 'import/no-unresolved': 'off',
+ 'no-param-reassign': 'off',
+ 'consistent-return': 'off',
+ 'global-require': 'off',
+ 'import/no-dynamic-require': 'off',
+ 'import/no-extraneous-dependencies': 'off',
+
+ // 4 行空格缩进
+ indent: ['error', 4, { SwitchCase: 1 }],
+
+ 'max-len': ['error', { code: 150 }],
+
+ 'operator-linebreak': 0,
+ },
+};
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
new file mode 100644
index 0000000..e69de29
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6acc570
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+dist
+.DS_Store
\ No newline at end of file
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..6c4479a
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,11 @@
+node_modules
+config
+demo
+src
+test
+.babelrc
+.editorconfig
+.travis.yml
+CHANGELOG.md
+package-lock.json
+TODO.md
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..efb0983
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,3 @@
+language: node_js
+node_js:
+ - "8"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..420e6f2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1 @@
+# Change Log
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..431a2b0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,128 @@
+# [qrcode-decoder](https://github.com/yugasun/qrcode-decoder)
+
+[](https://github.com/yugasun/qrcode-decoder/blob/master/LICENSE)
+[](https://travis-ci.org/yugasun/qrcode-decoder)
+[](http://www.npmtrends.com/qrcode-decoder)
+
+[简体中文](./README.zh-CN.md) | English
+
+A tool for decoding qrcode.
+
+## Directory
+
+```
+.
+├── demo code demo
+├── dist build output
+├── doc docs
+├── src source code
+├── test unit test
+├── CHANGELOG.md change log
+└── TODO.md todo list
+```
+
+## Guide
+
+Use `npm` to install.
+
+```bash
+$ npm install --save qrcode-decoder
+```
+
+Using in webpack:
+
+```js
+import QrcodeDecoder from 'qrcode-decoder';
+```
+
+Using in browser:
+
+```html
+
+```
+
+## Demo
+
+### QrcodeDecoder()
+
+User `new` to create a decoder object.
+
+```javascript
+var qr = new QrcodeDecoder();
+```
+
+#### decodeFromImage(img, options)
+
+Decodes an image from url or an `
` element with a `src` attribute set.
+
+```javascript
+qr.decodeFromImage(img).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/image.html)
+
+#### decodeFromVideo(videoElem, options)
+
+Decodes directly from a video with a well specified `src` attribute
+
+```javascript
+qr.decodeFromVideo(videoElement).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/video.html)
+
+#### decodeFromCamera(videoElem, options)
+
+Decodes from a videoElement.
+
+```javascript
+qr.decodeFromCamera(videoElem).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/camera.html)
+
+#### stop()
+
+Stops the current `qr` from searching for a QRCode.
+
+## Develop
+
+Install dependencies:
+
+```bash
+$ npm install
+```
+
+Build code:
+
+```bash
+$ npm run build
+```
+
+Run unit test:
+
+```bash
+$ npm test
+```
+
+Modify version in `package.json`, run `release` script:
+
+```bash
+$ npm run release
+```
+
+Publish
+
+```bash
+$ npm publish
+```
+
+## License
+
+[MIT](./LICENSE)
diff --git a/README.zh-CN.md b/README.zh-CN.md
new file mode 100644
index 0000000..786de35
--- /dev/null
+++ b/README.zh-CN.md
@@ -0,0 +1,128 @@
+# [qrcode-decoder](https://github.com/yugasun/qrcode-decoder)
+
+[](https://github.com/yugasun/qrcode-decoder/blob/master/LICENSE)
+[](https://travis-ci.org/yugasun/qrcode-decoder)
+[](http://www.npmtrends.com/qrcode-decoder)
+
+简体中文 | [English](./README.md)
+
+二维码解析工具。
+
+## 目录介绍
+
+```
+.
+├── demo 使用demo
+├── dist 编译产出代码
+├── doc 项目文档
+├── src 源代码目录
+├── test 单元测试
+├── CHANGELOG.md 变更日志
+└── TODO.md 计划功能
+```
+
+## 使用者指南
+
+通过 npm 下载安装代码
+
+```bash
+$ npm install --save qrcode-decoder
+```
+
+如果你是 webpack 等环境
+
+```js
+import QrcodeDecoder from 'qrcode-decoder';
+```
+
+如果你是浏览器环境
+
+```html
+
+```
+
+## 示例
+
+### QrcodeDecoder()
+
+通过 `new` 关键字生成处理对象。
+
+```javascript
+var qr = new QrcodeDecoder();
+```
+
+#### decodeFromImage(img, options)
+
+解析页面中的图片二维码。
+
+```javascript
+qr.decodeFromImage(img).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/image.html)
+
+#### decodeFromVideo(videoElem, options)
+
+解析页面中的视频中的二维码。
+
+```javascript
+qr.decodeFromVideo(videoElement).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/video.html)
+
+#### decodeFromCamera(videoElem, options)
+
+通过获取摄像头视频来扫描解析二维码。
+
+```javascript
+qr.decodeFromCamera(videoElem).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/camera.html)
+
+#### stop()
+
+停止当前视频捕获。
+
+## 开发
+
+首次运行需要先安装依赖
+
+```bash
+$ npm install
+```
+
+一键打包生成生产代码
+
+```bash
+$ npm run build
+```
+
+运行单元测试,浏览器环境需要手动测试,位于`test/browser`
+
+```bash
+$ npm test
+```
+
+修改 package.json 中的版本号,修改 README.md 中的版本号,修改 CHANGELOG.md,然后发布新版
+
+```bash
+$ npm run release
+```
+
+将新版本发布到 npm
+
+```bash
+$ npm publish
+```
+
+## License
+
+[MIT](./LICENSE)
diff --git a/TODO.md b/TODO.md
new file mode 100644
index 0000000..5a0521d
--- /dev/null
+++ b/TODO.md
@@ -0,0 +1,5 @@
+# TODO
+
+Rewrite unit test.
+
+- [] Rewrite unit test.
diff --git a/config/rollup.config.aio.js b/config/rollup.config.aio.js
new file mode 100644
index 0000000..14b9e63
--- /dev/null
+++ b/config/rollup.config.aio.js
@@ -0,0 +1,32 @@
+// rollup.config.js
+
+var babel = require('rollup-plugin-babel');
+var nodeResolve = require('rollup-plugin-node-resolve');
+var commonjs = require('rollup-plugin-commonjs');
+
+var common = require('./rollup.js');
+
+export default {
+ input: 'src/index.js',
+ output: {
+ file: 'dist/index.aio.js',
+ format: 'umd',
+ // 如果不同时使用 export 与 export default 可打开legacy
+ // legacy: true,
+ // name: common.name,
+ name: 'QrcodeDecoder',
+ banner: common.banner,
+ },
+ plugins: [
+ nodeResolve({
+ main: true,
+ }),
+ commonjs({
+ include: 'node_modules/**',
+ }),
+ babel({
+ runtimeHelpers: true,
+ exclude: 'node_modules/**',
+ }),
+ ],
+};
diff --git a/config/rollup.config.esm.js b/config/rollup.config.esm.js
new file mode 100644
index 0000000..c2d211c
--- /dev/null
+++ b/config/rollup.config.esm.js
@@ -0,0 +1,7 @@
+import config from './rollup.config';
+
+// ES output
+config.output.format = 'es';
+config.output.file = 'dist/index.esm.js';
+
+export default config;
diff --git a/config/rollup.config.js b/config/rollup.config.js
new file mode 100644
index 0000000..ea3f1a9
--- /dev/null
+++ b/config/rollup.config.js
@@ -0,0 +1,21 @@
+// rollup.config.js
+
+var babel = require('rollup-plugin-babel');
+var common = require('./rollup.js');
+
+export default {
+ input: 'src/index.js',
+ output: {
+ file: 'dist/index.js',
+ format: 'cjs',
+ // 如果不同时使用 export 与 export default 可打开legacy
+ // legacy: true,
+ banner: common.banner,
+ },
+ plugins: [
+ babel({
+ runtimeHelpers: true,
+ exclude: 'node_modules/**'
+ })
+ ]
+};
diff --git a/config/rollup.js b/config/rollup.js
new file mode 100644
index 0000000..78a7373
--- /dev/null
+++ b/config/rollup.js
@@ -0,0 +1,17 @@
+var pkg = require('../package.json');
+
+// 兼容 qrcode-decoder 和 @yugasun/qrcode-decoder
+var name = pkg.name.split('/').pop();
+var version = pkg.version;
+
+var banner =
+`/*!
+ * qrcode-decoder ${version} (https://github.com/yugasun/qrcode-decoder)
+ * API https://github.com/yugasun/qrcode-decoder/blob/master/doc/api.md
+ * Copyright 2017-${(new Date).getFullYear()} yugasun. All Rights Reserved
+ * Licensed under MIT (https://github.com/yugasun/qrcode-decoder/blob/master/LICENSE)
+ */
+`;
+
+exports.name = name;
+exports.banner = banner;
diff --git a/demo/camera.html b/demo/camera.html
new file mode 100644
index 0000000..97f38cb
--- /dev/null
+++ b/demo/camera.html
@@ -0,0 +1,39 @@
+
+
+
+ QrcodeDecoder - Camera
+
+
+
+ Click start to scan qrcode.
+
+
+
+
+
+
+
diff --git a/demo/image.html b/demo/image.html
new file mode 100644
index 0000000..89f7e5c
--- /dev/null
+++ b/demo/image.html
@@ -0,0 +1,31 @@
+
+
+
+
+ QrcodeDecoder - Image
+
+
+
+
+
+
+
+
+
diff --git a/demo/index.html b/demo/index.html
new file mode 100644
index 0000000..1a731af
--- /dev/null
+++ b/demo/index.html
@@ -0,0 +1,18 @@
+
+
+
+
+ QrcodeDecoder
+
+
+
+ QrcodeDecoder
+ Examples
+
+
+
+
diff --git a/demo/video.html b/demo/video.html
new file mode 100644
index 0000000..b7e3ca6
--- /dev/null
+++ b/demo/video.html
@@ -0,0 +1,39 @@
+
+
+
+
+ QrcodeDecoder - Video
+
+
+
+ Click start to scan qrcode.
+
+
+
+
+
+
+
diff --git a/doc/api.md b/doc/api.md
new file mode 100644
index 0000000..c8d053a
--- /dev/null
+++ b/doc/api.md
@@ -0,0 +1,49 @@
+# Api
+
+### QrcodeDecoder()
+
+User `new` to create a decoder object.
+
+```javascript
+var qr = new QrcodeDecoder();
+```
+
+#### decodeFromImage(img, options)
+
+Decodes an image from url or an `
` element with a `src` attribute set.
+
+```javascript
+qr.decodeFromImage(img).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/image.html)
+
+#### decodeFromVideo(videoElem, options)
+
+Decodes directly from a video with a well specified `src` attribute
+
+```javascript
+qr.decodeFromVideo(videoElement).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/video.html)
+
+#### decodeFromCamera(videoElem, options)
+
+Decodes from a videoElement.
+
+```javascript
+qr.decodeFromCamera(videoElem).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/camera.html)
+
+#### stop()
+
+Stops the current `qr` from searching for a QRCode.
\ No newline at end of file
diff --git a/doc/api.zh-CN.md b/doc/api.zh-CN.md
new file mode 100644
index 0000000..9ade3ca
--- /dev/null
+++ b/doc/api.zh-CN.md
@@ -0,0 +1,49 @@
+# Api
+
+### QrcodeDecoder()
+
+通过 `new` 关键字生成处理对象。
+
+```javascript
+var qr = new QrcodeDecoder();
+```
+
+#### decodeFromImage(img, options)
+
+解析页面中的图片二维码。
+
+```javascript
+qr.decodeFromImage(img).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/image.html)
+
+#### decodeFromVideo(videoElem, options)
+
+解析页面中的视频中的二维码。
+
+```javascript
+qr.decodeFromVideo(videoElement).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/video.html)
+
+#### decodeFromCamera(videoElem, options)
+
+通过获取摄像头视频来扫描解析二维码。
+
+```javascript
+qr.decodeFromCamera(videoElem).then((res) => {
+ console.log(res);
+});
+```
+
+[Demo](./demo/camera.html)
+
+#### stop()
+
+停止当前视频捕获。
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..8dd1e39
--- /dev/null
+++ b/package.json
@@ -0,0 +1,54 @@
+{
+ "name": "qrcode-decoder",
+ "version": "1.1.0",
+ "description": "Tool for decoding qrcode",
+ "main": "dist/index.js",
+ "jsnext:main": "dist/index.esm.js",
+ "module": "dist/index.esm.js",
+ "sideEffects": false,
+ "scripts": {
+ "clean": "rimraf ./dist",
+ "lint": "eslint src",
+ "build:self": "rollup -c config/rollup.config.js",
+ "build:esm": "rollup -c config/rollup.config.esm.js",
+ "build:aio": "rollup -c config/rollup.config.aio.js",
+ "build": "npm run clean && npm run build:self && npm run build:esm && npm run build:aio",
+ "test": "npm run lint && npm run build",
+ "zuul-test": "zuul --local 8080 --ui mocha-bdd -- test/test.js",
+ "release": "git commit -am $npm_package_version && git tag $npm_package_version && git push && git push --tags"
+ },
+ "author": "yugasun",
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/yugasun/qrcode-decoder.git"
+ },
+ "bugs": {
+ "url": "https://github.com/yugasun/qrcode-decoder/issues"
+ },
+ "devDependencies": {
+ "babel-core": "6.26.0",
+ "babel-plugin-transform-runtime": "6.23.0",
+ "babel-preset-env": "1.6.1",
+ "babel-preset-stage-2": "^6.24.1",
+ "cdkit": "1.1.0",
+ "es5-shim": "4.5.10",
+ "eslint": "4.18.2",
+ "eslint-config-airbnb-base": "^13.1.0",
+ "eslint-plugin-import": "^2.14.0",
+ "expect.js": "0.3.1",
+ "jsdom": "^13.0.0",
+ "mocha": "3.5.3",
+ "mocha-jsdom": "^2.0.0",
+ "rimraf": "2.6.2",
+ "rollup": "0.57.1",
+ "rollup-plugin-babel": "3.0.3",
+ "rollup-plugin-commonjs": "8.3.0",
+ "rollup-plugin-node-resolve": "3.0.3",
+ "zuul": "^3.12.0"
+ },
+ "dependencies": {
+ "babel-polyfill": "^6.26.0",
+ "jsqr": "^1.1.1"
+ }
+}
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..2b0d11e
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,370 @@
+import 'babel-polyfill';
+import jsQR from 'jsqr';
+/* eslint-disable no-underscore-dangle */
+class QrcodeDecoder {
+ /**
+ * Constructor for QrcodeDecoder
+ */
+ constructor() {
+ this.timerCapture = null;
+ this.canvasElem = null;
+ this.gCtx = null;
+ this.stream = null;
+ this.videoElem = null;
+ this.getUserMediaHandler = null;
+ this.videoConstraints = { video: true, audio: false };
+
+ this.defaultOption = { inversionAttempts: 'attemptBoth' };
+ }
+
+ /**
+ * Verifies if canvas element is supported.
+ */
+ /* eslint-disable class-methods-use-this */
+ isCanvasSupported() {
+ const elem = document.createElement('canvas');
+ return !!(elem.getContext && elem.getContext('2d'));
+ }
+
+ /**
+ * draw lint
+ *
+ * @param {object} begin line begin point
+ * @param {object} end line end point
+ * @param {string} color color string
+ */
+ // _drawLine(begin, end, color) {
+ // this.gCtx.beginPath();
+ // this.gCtx.moveTo(begin.x, begin.y);
+ // this.gCtx.lineTo(end.x, end.y);
+ // this.gCtx.lineWidth = 4;
+ // this.gCtx.strokeStyle = color;
+ // this.gCtx.stroke();
+ // }
+
+ /**
+ * create qrcode marker
+ *
+ * @param {object} code jsqr parse code object
+ */
+ // _createQrcodeMark(code) {
+ // this._drawLine(
+ // code.location.topLeftCorner,
+ // code.location.topRightCorner,
+ // '#FF3B58',
+ // );
+ // this._drawLine(
+ // code.location.topRightCorner,
+ // code.location.bottomRightCorner,
+ // '#FF3B58',
+ // );
+ // this._drawLine(
+ // code.location.bottomRightCorner,
+ // code.location.bottomLeftCorner,
+ // '#FF3B58',
+ // );
+ // this._drawLine(
+ // code.location.bottomLeftCorner,
+ // code.location.topLeftCorner,
+ // '#FF3B58',
+ // );
+ // }
+
+ _createImageData(target, width, height) {
+ if (!this.canvasElem) {
+ this._prepareCanvas(width, height);
+ }
+
+ this.gCtx.clearRect(0, 0, width, height);
+ this.gCtx.drawImage(target, 0, 0, width, height);
+
+ const imageData = this.gCtx.getImageData(
+ 0,
+ 0,
+ this.canvasElem.width,
+ this.canvasElem.height,
+ );
+
+ return imageData;
+ }
+
+ /**
+ * Prepares the canvas element (which will
+ * receive the image from the camera and provide
+ * what the algorithm needs for checking for a
+ * QRCode and then decoding it.)
+ *
+ *
+ * @param {DOMElement} canvasElem the canvas
+ * element
+ * @param {number} width The width that
+ * the canvas element
+ * should have
+ * @param {number} height The height that
+ * the canvas element
+ * should have
+ * @return {DOMElement} the canvas
+ * after the resize if width and height
+ * provided.
+ */
+ _prepareCanvas(width, height) {
+ if (!this.canvasElem) {
+ this.canvasElem = document.createElement('canvas');
+ this.canvasElem.style.width = `${width}px`;
+ this.canvasElem.style.height = `${height}px`;
+ this.canvasElem.width = width;
+ this.canvasElem.height = height;
+ }
+
+ this.gCtx = this.canvasElem.getContext('2d');
+ }
+
+ /**
+ * Based on the video dimensions and the canvas
+ * that was previously generated captures the
+ * video/image source and then paints into the
+ * canvas so that the decoder is able to work as
+ * it expects.
+ * @param {DOMElement} videoElem