├── .bowerrc ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── test_and_release.yml ├── .gitignore ├── .jshintignore ├── .jshintrc ├── .npmignore ├── Gruntfile.js ├── README.md ├── bower.json ├── dashcore-node ├── index.js └── service.js ├── docker ├── Dockerfile └── dashcore-node.json ├── lib └── test │ ├── .eslintrc │ ├── pages │ ├── BlockPage.js │ ├── StatusPage.js │ └── TopPanel.js │ └── util │ └── wait.js ├── package-lock.json ├── package.json ├── po ├── de_DE.po ├── es.po ├── fr_FR.po ├── ja.po ├── nl.po ├── pt_BR.po └── zh.po ├── public ├── css │ └── main.min.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── img │ ├── .gitignore │ ├── DASHlogo_white.png │ ├── angularjs.png │ ├── icons │ │ ├── copy.png │ │ └── favicon.ico │ ├── leveldb.png │ ├── loading.gif │ ├── logo.svg │ └── nodejs.png ├── index.html ├── js │ ├── angularjs-all.min.js │ ├── main.min.js │ └── vendors.min.js ├── robots.txt ├── sound │ └── transaction.mp3 ├── src │ ├── css │ │ └── common.css │ └── js │ │ ├── app.js │ │ ├── config.js │ │ ├── controllers │ │ ├── address.js │ │ ├── blocks.js │ │ ├── connection.js │ │ ├── currency.js │ │ ├── footer.js │ │ ├── header.js │ │ ├── index.js │ │ ├── messages.js │ │ ├── scanner.js │ │ ├── search.js │ │ ├── status.js │ │ └── transactions.js │ │ ├── directives.js │ │ ├── filters.js │ │ ├── init.js │ │ ├── ios-imagefile-megapixel │ │ └── megapix-image.js │ │ ├── jsqrcode │ │ ├── alignpat.js │ │ ├── bitmat.js │ │ ├── bmparser.js │ │ ├── datablock.js │ │ ├── databr.js │ │ ├── datamask.js │ │ ├── decoder.js │ │ ├── detector.js │ │ ├── errorlevel.js │ │ ├── findpat.js │ │ ├── formatinf.js │ │ ├── gf256.js │ │ ├── gf256poly.js │ │ ├── grid.js │ │ ├── qrcode.js │ │ ├── rsdecoder.js │ │ ├── test.html │ │ └── version.js │ │ ├── services │ │ ├── address.js │ │ ├── blocks.js │ │ ├── currency.js │ │ ├── global.js │ │ ├── hash.js │ │ ├── socket.js │ │ ├── status.js │ │ └── transactions.js │ │ └── translations.js └── views │ ├── 404.html │ ├── address.html │ ├── block.html │ ├── block_list.html │ ├── dummy-translations.html │ ├── includes │ ├── connection.html │ ├── currency.html │ ├── header.html │ ├── infoStatus.html │ └── search.html │ ├── index.html │ ├── messages_verify.html │ ├── redirect.html │ ├── status.html │ ├── transaction.html │ ├── transaction │ ├── list.html │ └── tx.html │ └── transaction_sendraw.html └── test ├── .eslintrc └── e2e ├── conf.js └── test.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | end_of_line = lf 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dashcore-node 2 | public 3 | Gruntfile.js 4 | node_modules 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "rules": { 4 | "no-plusplus": 0, 5 | "eol-last": [ 6 | "error", 7 | "always" 8 | ], 9 | "class-methods-use-this": "off", 10 | "curly": [ 11 | "error", 12 | "all" 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/workflows/test_and_release.yml: -------------------------------------------------------------------------------- 1 | name: Test and Release 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: 7 | - published 8 | pull_request: 9 | branches: 10 | - master 11 | - v[0-9]+.[0-9]+-dev 12 | 13 | jobs: 14 | test: 15 | name: Run Insight UI tests 16 | runs-on: ubuntu-20.04 17 | timeout-minutes: 10 18 | steps: 19 | - name: Cancel Previous Runs 20 | uses: styfle/cancel-workflow-action@0.9.0 21 | with: 22 | access_token: ${{ github.token }} 23 | 24 | - uses: actions/checkout@v2 25 | 26 | - uses: actions/setup-node@v2 27 | with: 28 | node-version: '20' 29 | 30 | - name: Enable NPM cache 31 | uses: actions/cache@v2 32 | with: 33 | path: '~/.npm' 34 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 35 | restore-keys: | 36 | ${{ runner.os }}-node- 37 | 38 | - name: Check NPM package lock version is updated 39 | uses: dashevo/gh-action-check-package-lock@v1 40 | 41 | - name: Install NPM dependencies 42 | run: npm ci 43 | 44 | - name: Run ESLinter 45 | run: npm run lint 46 | 47 | - name: Use https instead of git protocol fo git 48 | run: git config --global url."https://".insteadOf git:// 49 | 50 | - name: Build project 51 | run: npm run build 52 | 53 | - name: Run tests 54 | run: npm run test 55 | 56 | release-npm: 57 | name: Release NPM package 58 | runs-on: ubuntu-20.04 59 | needs: test 60 | if: ${{ github.event_name == 'release' }} 61 | steps: 62 | - uses: actions/checkout@v2 63 | 64 | - name: Check package version matches tag 65 | uses: geritol/match-tag-to-package-version@0.1.0 66 | env: 67 | TAG_PREFIX: refs/tags/v 68 | 69 | - name: Enable NPM cache 70 | uses: actions/cache@v2 71 | with: 72 | path: '~/.npm' 73 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 74 | restore-keys: | 75 | ${{ runner.os }}-node- 76 | 77 | - name: Install NPM dependencies 78 | run: npm ci 79 | 80 | - name: Set release tag 81 | uses: actions/github-script@v3 82 | id: tag 83 | with: 84 | result-encoding: string 85 | script: | 86 | const tag = context.payload.release.tag_name; 87 | const [, major, minor] = tag.match(/^v([0-9]+)\.([0-9]+)/); 88 | return (tag.includes('dev') ? `${major}.${minor}-dev` : 'latest'); 89 | 90 | - name: Use https instead of git protocol fo git 91 | run: git config --global url."https://".insteadOf git:// 92 | 93 | - name: Publish NPM package 94 | uses: JS-DevTools/npm-publish@v1 95 | with: 96 | token: ${{ secrets.NPM_TOKEN }} 97 | tag: ${{ steps.tag.outputs.result }} 98 | 99 | release-docker: 100 | name: Release Docker image 101 | runs-on: ubuntu-20.04 102 | needs: release-npm 103 | if: ${{ github.event_name == 'release' }} 104 | steps: 105 | - uses: actions/checkout@v2 106 | 107 | - name: Set up QEMU 108 | uses: docker/setup-qemu-action@v1 109 | 110 | - name: Set up Docker BuildX 111 | uses: docker/setup-buildx-action@v1 112 | with: 113 | version: v0.7.0 114 | install: true 115 | driver-opts: image=moby/buildkit:buildx-stable-1 116 | 117 | - name: Login to DockerHub 118 | uses: docker/login-action@v1 119 | with: 120 | username: ${{ secrets.DOCKERHUB_USERNAME }} 121 | password: ${{ secrets.DOCKERHUB_TOKEN }} 122 | 123 | - name: Set suffix to Docker tags 124 | uses: actions/github-script@v3 125 | id: suffix 126 | with: 127 | result-encoding: string 128 | script: "return (context.payload.release.tag_name.includes('-dev') ? '-dev' : '');" 129 | 130 | - name: Set release tag 131 | uses: actions/github-script@v3 132 | id: tag 133 | with: 134 | result-encoding: string 135 | script: | 136 | const tag = context.payload.release.tag_name; 137 | const [, major] = tag.match(/^v([0-9]+)/); 138 | return major; 139 | 140 | - name: Set Docker tags and labels 141 | id: docker_meta 142 | uses: docker/metadata-action@v3 143 | with: 144 | images: dashpay/insight 145 | tags: | 146 | type=match,pattern=v(\d+),group=1 147 | type=match,pattern=v(\d+.\d+),group=1 148 | type=match,pattern=v(\d+.\d+.\d+),group=1 149 | type=match,pattern=v(.*),group=1,suffix=,enable=${{ contains(github.event.release.tag_name, '-dev') }} 150 | flavor: | 151 | latest=${{ !contains(github.event.release.tag_name, '-dev') }} 152 | suffix=${{ steps.suffix.outputs.result }} 153 | 154 | - name: Build and push Docker image 155 | id: docker_build 156 | uses: docker/build-push-action@v2 157 | with: 158 | context: docker 159 | file: docker/Dockerfile 160 | push: true 161 | tags: ${{ steps.docker_meta.outputs.tags }} 162 | labels: ${{ steps.docker_meta.outputs.labels }} 163 | cache-from: type=gha 164 | cache-to: type=gha,mode=max 165 | platforms: linux/amd64,linux/arm64 166 | build-args: | 167 | VERSION=${{ github.event.release.tag_name }} 168 | MAJOR_VERSION=${{ steps.tag.outputs.result }} 169 | 170 | - name: Output Docker image digest 171 | run: echo ${{ steps.docker_build.outputs.digest }} 172 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # from https://github.com/github/gitignore/blob/master/Node.gitignore 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | *.swp 11 | tags 12 | pids 13 | logs 14 | results 15 | build 16 | .idea 17 | node_modules 18 | 19 | # extras 20 | *.swp 21 | *.swo 22 | *~ 23 | .project 24 | peerdb.json 25 | 26 | npm-debug.log 27 | .nodemonignore 28 | 29 | .DS_Store 30 | public/lib/* 31 | !public/lib/zeroclipboard/ZeroClipboard.swf 32 | db/txs/* 33 | db/txs 34 | db/testnet/txs/* 35 | db/testnet/txs 36 | db/blocks/* 37 | db/blocks 38 | db/testnet/blocks/* 39 | db/testnet/blocks 40 | 41 | public/js/angularjs-all.js 42 | public/js/main.js 43 | public/js/vendors.js 44 | 45 | public/css/main.css 46 | 47 | README.html 48 | po/* 49 | !po/*.po 50 | .idea 51 | -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | lib 2 | test 3 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. 3 | "browser": true, // Standard browser globals e.g. `window`, `document`. 4 | "esnext": true, // Allow ES.next specific features such as `const` and `let`. 5 | "bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.). 6 | "camelcase": false, // Permit only camelcase for `var` and `object indexes`. 7 | "curly": false, // Require {} for every new block or scope. 8 | "eqeqeq": true, // Require triple equals i.e. `===`. 9 | "immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 10 | "latedef": true, // Prohibit variable use before definition. 11 | "newcap": true, // Require capitalization of all constructor functions e.g. `new F()`. 12 | "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`. 13 | "quotmark": "single", // Define quotes to string values. 14 | "regexp": true, // Prohibit `.` and `[^...]` in regular expressions. 15 | "undef": true, // Require all non-global variables be declared before they are used. 16 | "unused": true, // Warn unused variables. 17 | "strict": true, // Require `use strict` pragma in every file. 18 | "trailing": true, // Prohibit trailing whitespaces. 19 | "smarttabs": false, // Suppresses warnings about mixed tabs and spaces 20 | "globals": { // Globals variables. 21 | "angular": true 22 | }, 23 | "predef": [ // Extra globals. 24 | "define", 25 | "require", 26 | "exports", 27 | "module", 28 | "describe", 29 | "before", 30 | "beforeEach", 31 | "after", 32 | "afterEach", 33 | "it", 34 | "inject", 35 | "$", 36 | "io", 37 | "app", 38 | "moment" 39 | ], 40 | "indent": false, // Specify indentation spacing 41 | "devel": true, // Allow development statements e.g. `console.log();`. 42 | "noempty": true // Prohibit use of empty blocks. 43 | } 44 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.swp 10 | tags 11 | pids 12 | logs 13 | results 14 | build 15 | lib/test 16 | test 17 | 18 | node_modules 19 | 20 | # extras 21 | *.swp 22 | *.swo 23 | *~ 24 | .project 25 | peerdb.json 26 | 27 | npm-debug.log 28 | .nodemonignore 29 | 30 | .DS_Store 31 | db/txs/* 32 | db/txs 33 | db/testnet/txs/* 34 | db/testnet/txs 35 | db/blocks/* 36 | db/blocks 37 | db/testnet/blocks/* 38 | db/testnet/blocks 39 | 40 | README.html 41 | k* 42 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | 5 | //Load NPM tasks 6 | grunt.loadNpmTasks('grunt-contrib-uglify'); 7 | grunt.loadNpmTasks('grunt-contrib-concat'); 8 | grunt.loadNpmTasks('grunt-contrib-watch'); 9 | grunt.loadNpmTasks('grunt-css'); 10 | grunt.loadNpmTasks('grunt-markdown'); 11 | grunt.loadNpmTasks('grunt-macreload'); 12 | grunt.loadNpmTasks('grunt-angular-gettext'); 13 | 14 | // Project Configuration 15 | grunt.initConfig({ 16 | pkg: grunt.file.readJSON('package.json'), 17 | concat: { 18 | options: { 19 | process: function(src, filepath) { 20 | if (filepath.substr(filepath.length - 2) === 'js') { 21 | return '// Source: ' + filepath + '\n' + 22 | src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1'); 23 | } else { 24 | return src; 25 | } 26 | } 27 | }, 28 | vendors: { 29 | src: ['public/src/js/ios-imagefile-megapixel/megapix-image.js', 'public/lib/qrcode-generator/js/qrcode.js', 'public/src/js/jsqrcode/grid.js', 'public/src/js/jsqrcode/version.js', 'public/src/js/jsqrcode/detector.js', 'public/src/js/jsqrcode/formatinf.js', 'public/src/js/jsqrcode/errorlevel.js', 'public/src/js/jsqrcode/bitmat.js', 'public/src/js/jsqrcode/datablock.js', 'public/src/js/jsqrcode/bmparser.js', 'public/src/js/jsqrcode/datamask.js', 'public/src/js/jsqrcode/rsdecoder.js', 'public/src/js/jsqrcode/gf256poly.js', 'public/src/js/jsqrcode/gf256.js', 'public/src/js/jsqrcode/decoder.js', 'public/src/js/jsqrcode/qrcode.js', 'public/src/js/jsqrcode/findpat.js', 'public/src/js/jsqrcode/alignpat.js', 'public/src/js/jsqrcode/databr.js', 'public/lib/momentjs/min/moment.min.js', 'public/lib/moment/lang/es.js', 'public/lib/zeroclipboard/ZeroClipboard.min.js'], 30 | dest: 'public/js/vendors.js' 31 | }, 32 | angular: { 33 | src: ['public/lib/angular/angular.min.js', 'public/lib/angular-resource/angular-resource.min.js', 'public/lib/angular-route/angular-route.min.js', 'public/lib/angular-qrcode/qrcode.js', 'public/lib/angular-animate/angular-animate.min.js', 'public/lib/angular-bootstrap/ui-bootstrap.js', 'public/lib/angular-bootstrap/ui-bootstrap-tpls.js', 'public/lib/angular-ui-utils/ui-utils.min.js', 'public/lib/ngprogress/build/ngprogress.min.js', 'public/lib/angular-gettext/dist/angular-gettext.min.js', 'public/lib/angular-moment/angular-moment.min.js', 'public/lib/angular-ui-router/release/angular-ui-router.js'], 34 | dest: 'public/js/angularjs-all.js' 35 | }, 36 | main: { 37 | src: ['public/src/js/app.js', 'public/src/js/controllers/*.js', 'public/src/js/services/*.js', 'public/src/js/directives.js', 'public/src/js/filters.js', 'public/src/js/config.js', 'public/src/js/init.js', 'public/src/js/translations.js'], 38 | dest: 'public/js/main.js' 39 | }, 40 | css: { 41 | src: ['public/lib/bootstrap/dist/css/bootstrap.min.css', 'public/src/css/**/*.css'], 42 | dest: 'public/css/main.css' 43 | } 44 | }, 45 | uglify: { 46 | options: { 47 | banner: '/*! <%= pkg.name %> <%= pkg.version %> */\n', 48 | mangle: false 49 | }, 50 | vendors: { 51 | src: 'public/js/vendors.js', 52 | dest: 'public/js/vendors.min.js' 53 | }, 54 | angular: { 55 | src: 'public/js/angularjs-all.js', 56 | dest: 'public/js/angularjs-all.min.js' 57 | }, 58 | main: { 59 | src: 'public/js/main.js', 60 | dest: 'public/js/main.min.js' 61 | } 62 | }, 63 | cssmin: { 64 | css: { 65 | src: 'public/css/main.css', 66 | dest: 'public/css/main.min.css' 67 | } 68 | }, 69 | markdown: { 70 | all: { 71 | files: [ 72 | { 73 | expand: true, 74 | src: 'README.md', 75 | dest: '.', 76 | ext: '.html' 77 | } 78 | ] 79 | } 80 | }, 81 | macreload: { 82 | chrome: { 83 | browser: 'chrome', 84 | editor: 'macvim' 85 | } 86 | }, 87 | watch: { 88 | main: { 89 | files: ['public/src/js/**/*.js'], 90 | tasks: ['concat:main', 'uglify:main'], 91 | }, 92 | css: { 93 | files: ['public/src/css/**/*.css'], 94 | tasks: ['concat:css', 'cssmin'], 95 | }, 96 | }, 97 | nggettext_extract: { 98 | pot: { 99 | files: { 100 | 'po/template.pot': ['public/views/*.html', 'public/views/**/*.html'] 101 | } 102 | }, 103 | }, 104 | nggettext_compile: { 105 | all: { 106 | options: { 107 | module: 'insight' 108 | }, 109 | files: { 110 | 'public/src/js/translations.js': ['po/*.po'] 111 | } 112 | }, 113 | } 114 | }); 115 | 116 | //Making grunt default to force in order not to break the project. 117 | grunt.option('force', true); 118 | 119 | //Default task(s). 120 | grunt.registerTask('default', ['watch']); 121 | 122 | //Update .pot file 123 | grunt.registerTask('translate', ['nggettext_extract']); 124 | 125 | //Compile task (concat + minify) 126 | grunt.registerTask('compile', ['nggettext_compile', 'concat', 'uglify', 'cssmin']); 127 | 128 | 129 | }; 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Insight UI 2 | 3 | A Dash blockchain explorer web application service for [Dashcore Node](https://github.com/dashevo/dashcore-node) using [Insight API](https://github.com/dashevo/insight-api). 4 | 5 | ## Quick Start 6 | 7 | Please see the guide at [https://bitcore.io/guides/full-node](https://bitcore.io/guides/full-node) for information about getting a block explorer running. This is only the front-end component of the block explorer, and is packaged together with all of the necessary components in [Dashcore](https://github.com/dashevo/dashcore). 8 | 9 | ## Getting Started 10 | 11 | To manually install all of the necessary components, you can run these commands: 12 | 13 | ```bash 14 | npm install -g @dashevo/dashcore-node 15 | dashcore-node create mynode 16 | cd mynode 17 | dashcore-node install @dashevo/insight-api 18 | dashcore-node install @dashevo/insight-ui 19 | dashcore-node start 20 | ``` 21 | 22 | Open a web browser to `http://localhost:3001/insight/` 23 | 24 | ## Development 25 | 26 | To run Insight UI Dash locally in development mode: 27 | 28 | Install dependencies: 29 | 30 | ``` 31 | $ npm install 32 | ``` 33 | 34 | To download bower dependencies, compile and minify the web application's assets: 35 | 36 | ``` 37 | $ npm run build 38 | ``` 39 | 40 | There is a convenient Gruntfile.js for automation during editing the code 41 | 42 | ``` 43 | $ npm run watch 44 | ``` 45 | 46 | ## Multilanguage support 47 | 48 | Insight UI Dash uses [angular-gettext](http://angular-gettext.rocketeer.be) for multilanguage support. 49 | 50 | To enable a text to be translated, add the ***translate*** directive to html tags. See more details [here](http://angular-gettext.rocketeer.be/dev-guide/annotate/). Then, run: 51 | 52 | ``` 53 | npm run build 54 | ``` 55 | 56 | This action will create a template.pot file in ***po/*** folder. You can open it with some PO editor ([Poedit](http://poedit.net)). Read this [guide](http://angular-gettext.rocketeer.be/dev-guide/translate/) to learn how to edit/update/import PO files from a generated POT file. PO file will be generated inside po/ folder. 57 | 58 | If you make new changes, simply run **grunt compile** again to generate a new .pot template and the angular javascript ***js/translations.js***. Then (if use Poedit), open .po file and choose ***update from POT File*** from **Catalog** menu. 59 | 60 | Finally changes your default language from ***public/src/js/config*** 61 | 62 | ``` 63 | gettextCatalog.currentLanguage = 'es'; 64 | ``` 65 | 66 | This line will take a look at any *.po files inside ***po/*** folder, e.g. 67 | **po/es.po**, **po/nl.po**. After any change do not forget to run ***grunt 68 | compile***. 69 | 70 | 71 | ## Note 72 | 73 | For more details about the [Insight API](https://github.com/dashevo/insight-api) configuration and end-points, go to [Insight API GitHub repository](https://github.com/dashevo/insight-api). 74 | 75 | ## Contribute 76 | 77 | Contributions and suggestions are welcomed at the [Insight UI Dash GitHub repository](https://github.com/dashevo/insight-ui). 78 | 79 | 80 | ## License 81 | (The MIT License) 82 | 83 | Permission is hereby granted, free of charge, to any person obtaining 84 | a copy of this software and associated documentation files (the 85 | 'Software'), to deal in the Software without restriction, including 86 | without limitation the rights to use, copy, modify, merge, publish, 87 | distribute, sublicense, and/or sell copies of the Software, and to 88 | permit persons to whom the Software is furnished to do so, subject to 89 | the following conditions: 90 | 91 | The above copyright notice and this permission notice shall be 92 | included in all copies or substantial portions of the Software. 93 | 94 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 95 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 96 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 97 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 98 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 99 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 100 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 101 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "insight-ui-dash", 3 | "version": "2.0.0", 4 | "dependencies": { 5 | "angular": "1.7.6", 6 | "angular-animate": "1.7.6", 7 | "angular-bootstrap": "~2.5.0", 8 | "angular-gettext": "~2.3.11", 9 | "angular-moment": "~1.3.0", 10 | "angular-qrcode": "~3.1.0", 11 | "angular-resource": "1.7.6", 12 | "angular-route": "1.7.6", 13 | "angular-ui-router": "~1.0.20", 14 | "angular-ui-utils": "~0.1.1", 15 | "bootstrap": "~3.4.0", 16 | "momentjs": "~2.21.0", 17 | "ngprogress": "~1.1.3" 18 | }, 19 | "resolutions": { 20 | "angular": "1.7.6" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dashcore-node/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var BaseService = require('./service'); 4 | var inherits = require('util').inherits; 5 | var fs = require('fs'); 6 | 7 | var InsightUI = function(options) { 8 | BaseService.call(this, options); 9 | if (typeof options.apiPrefix !== 'undefined') { 10 | this.apiPrefix = options.apiPrefix; 11 | } else { 12 | this.apiPrefix = 'insight-api'; 13 | } 14 | if (typeof options.routePrefix !== 'undefined') { 15 | this.routePrefix = options.routePrefix; 16 | } else { 17 | this.routePrefix = 'insight'; 18 | } 19 | }; 20 | 21 | InsightUI.dependencies = ['@dashevo/insight-api']; 22 | 23 | inherits(InsightUI, BaseService); 24 | 25 | InsightUI.prototype.start = function(callback) { 26 | this.indexFile = this.filterIndexHTML(fs.readFileSync(__dirname + '/../public/index.html', {encoding: 'utf8'})); 27 | setImmediate(callback); 28 | }; 29 | 30 | InsightUI.prototype.getRoutePrefix = function() { 31 | return this.routePrefix; 32 | }; 33 | 34 | InsightUI.prototype.setupRoutes = function(app, express) { 35 | var self = this; 36 | 37 | app.use('/', function(req, res, next){ 38 | if (req.headers.accept && req.headers.accept.indexOf('text/html') !== -1 && 39 | req.headers["X-Requested-With"] !== 'XMLHttpRequest' 40 | ) { 41 | res.setHeader('Content-Type', 'text/html'); 42 | res.send(self.indexFile); 43 | } else { 44 | express.static(__dirname + '/../public')(req, res, next); 45 | } 46 | }); 47 | }; 48 | 49 | InsightUI.prototype.filterIndexHTML = function(data) { 50 | var transformed = data 51 | .replace(/apiPrefix = '\/api'/, "apiPrefix = '/" + this.apiPrefix + "'"); 52 | 53 | if (this.routePrefix) { 54 | transformed = transformed.replace(/ blockId.getText(); 24 | 25 | this.getBlockHash = () => blockHash.getText(); 26 | 27 | this.getNumberOfTrxs = () => numberOfTrxs.getText(); 28 | 29 | this.getHeight = () => height.getText(); 30 | 31 | this.getBlockReward = () => blockReward.getText(); 32 | 33 | this.getTimestamp = () => timestamp.getText(); 34 | 35 | this.getMinedBy = () => minedBy.getText(); 36 | 37 | this.getMerkleRoot = () => merkleRoot.getText(); 38 | 39 | this.getPreviousBlock = () => previousBlock.getText(); 40 | 41 | this.getDifficulty = () => difficulty.getText(); 42 | 43 | this.getBits = () => bits.getText(); 44 | 45 | this.getSize = () => size.getText(); 46 | 47 | this.getVersion = () => version.getText(); 48 | 49 | this.getNonce = () => nonce.getText(); 50 | 51 | this.getNextBlock = () => nextBlock.getText(); 52 | 53 | this.getTrxHash = () => trxHash.getText(); 54 | } 55 | module.exports = BlockPage; 56 | -------------------------------------------------------------------------------- /lib/test/pages/StatusPage.js: -------------------------------------------------------------------------------- 1 | const { By } = require('selenium-webdriver'); 2 | 3 | function StatusPage(driver) { 4 | const syncProgress = driver.findElement(By.xpath('//table//tr[1]//td[2]')); 5 | const currentSyncStatus = driver.findElement(By.xpath('//table//tr[2]//td[2]')); 6 | const startDate = driver.findElement(By.xpath('//table//tr[3]//td[2]')); 7 | const initialBlockChainHeight = driver.findElement(By.xpath('//table//tr[5]//td[2]')); 8 | const syncedBlocks = driver.findElement(By.xpath('//table//tr[6]//td[2]')); 9 | const skippedBlocks = driver.findElement(By.xpath('//table//tr[7]//td[2]')); 10 | const syncType = driver.findElement(By.xpath('//table//tr[8]//td[2]')); 11 | 12 | const lastBlockHash = driver.findElement(By.xpath('//table[2]//tr[1]//td[2]')); 13 | const currentBlockchainTip = driver.findElement(By.xpath('//table[2]//tr[2]//td[2]')); 14 | 15 | const version = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[1]//td[2]")); 16 | const protocolVersion = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[2]//td[2]")); 17 | const blocks = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[3]//td[2]")); 18 | const timeOffset = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[4]//td[2]")); 19 | const connections = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[5]//td[2]")); 20 | const miningDifficulty = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[6]//td[2]")); 21 | const network = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[7]//td[2]")); 22 | const proxySetting = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[8]//td[2]")); 23 | const infoErrors = driver.findElement(By.xpath("//h2[contains(text(), 'Dash node information')]/../table//tr[9]//td[2]")); 24 | 25 | 26 | this.getSyncProgress = () => syncProgress.getText(); 27 | 28 | this.getCurrentSyncStatus = () => currentSyncStatus.getText(); 29 | 30 | this.getStartDate = () => startDate.getText(); 31 | 32 | this.getInitialBlockChainHeight = () => initialBlockChainHeight.getText(); 33 | 34 | this.getSkippedBlocks = () => skippedBlocks.getText(); 35 | 36 | this.getSyncedBlocks = () => syncedBlocks.getText(); 37 | 38 | this.getSyncType = () => syncType.getText(); 39 | 40 | this.getLastBlockHash = () => lastBlockHash.getText(); 41 | 42 | this.getCurrentBlockchainTip = () => currentBlockchainTip.getText(); 43 | 44 | this.getVersion = () => version.getText(); 45 | 46 | this.getProtocolVersion = () => protocolVersion.getText(); 47 | 48 | this.getBlocks = () => blocks.getText(); 49 | 50 | this.getTimeOffset = () => timeOffset.getText(); 51 | 52 | this.getConnections = () => connections.getText(); 53 | 54 | this.getMiningDifficulty = () => miningDifficulty.getText(); 55 | 56 | this.getNetwork = () => network.getText(); 57 | 58 | this.getProxySetting = () => proxySetting.getText(); 59 | 60 | this.getInfoErrors = () => infoErrors.getText(); 61 | } 62 | module.exports = StatusPage; 63 | -------------------------------------------------------------------------------- /lib/test/pages/TopPanel.js: -------------------------------------------------------------------------------- 1 | const { By } = require('selenium-webdriver'); 2 | 3 | function TopPanel(driver) { 4 | const openBlockPage = driver.findElement(By.xpath('//a[@href=\'blocks\']')); 5 | const openStatusPage = driver.findElement(By.xpath('//a[@href=\'status\']')); 6 | const search = driver.findElement(By.id('search')); 7 | 8 | this.search = (text) => { 9 | search.sendKeys(text); 10 | search.submit(); 11 | }; 12 | 13 | this.openBlockPage = () => { 14 | openBlockPage.click(); 15 | }; 16 | 17 | this.openStatusPage = () => { 18 | openStatusPage.click(); 19 | }; 20 | } 21 | module.exports = TopPanel; 22 | -------------------------------------------------------------------------------- /lib/test/util/wait.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Asynchronously wait for a specified number of milliseconds. 3 | * 4 | * @param {number} ms - Number of milliseconds to wait. 5 | * 6 | * @return {Promise} The promise to await on. 7 | */ 8 | async function wait(ms) { 9 | return new Promise(resolve => setTimeout(resolve, ms)); 10 | } 11 | 12 | module.exports = wait; 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dashevo/insight-ui", 3 | "description": "An open-source frontend for the Insight API. The Insight API provides you with a convenient, powerful and simple way to query and broadcast data on the Dash network and build your own services with it.", 4 | "version": "4.0.10", 5 | "repository": "git://github.com/dashevo/insight-ui.git", 6 | "contributors": [ 7 | { 8 | "name": "Matias Alejo Garcia", 9 | "email": "ematiu@gmail.com" 10 | }, 11 | { 12 | "name": "Manuel Araoz", 13 | "email": "manuelaraoz@gmail.com" 14 | }, 15 | { 16 | "name": "Mario Colque", 17 | "email": "colquemario@gmail.com" 18 | }, 19 | { 20 | "name": "Gustavo Cortez", 21 | "email": "cmgustavo83@gmail.com" 22 | }, 23 | { 24 | "name": "Juan Ignacio Sosa Lopez", 25 | "email": "bechilandia@gmail.com" 26 | }, 27 | { 28 | "name": "Anton Suprunchuk", 29 | "email": "antouhou@gmail.com" 30 | } 31 | ], 32 | "bugs": { 33 | "url": "https://github.com/dashevo/insight-ui/issues" 34 | }, 35 | "homepage": "https://github.com/dashevo/insight-ui", 36 | "license": "MIT", 37 | "keywords": [ 38 | "insight", 39 | "blockchain", 40 | "blockexplorer", 41 | "dash", 42 | "bitcore-dash", 43 | "front-end", 44 | "dashcore" 45 | ], 46 | "dashcoreNode": "dashcore-node", 47 | "scripts": { 48 | "build": "bower install && grunt compile", 49 | "watch": "grunt", 50 | "lint": "eslint .", 51 | "install-and-build": "npm install && npm run build", 52 | "test": "mocha ./test/e2e/test.js --timeout 60000", 53 | "prepublishOnly": "npm run build" 54 | }, 55 | "devDependencies": { 56 | "bower": "^1.8.14", 57 | "chai": "^4.2.0", 58 | "eslint": "^5.6.1", 59 | "eslint-config-airbnb-base": "^13.1.0", 60 | "eslint-plugin-import": "^2.13.0", 61 | "grunt": "~0.4.2", 62 | "grunt-angular-gettext": "^0.2.15", 63 | "grunt-cli": "~1.4.3", 64 | "grunt-contrib-concat": "~1.0.1", 65 | "grunt-contrib-jshint": "~2.0.0", 66 | "grunt-contrib-uglify": "~5.2.0", 67 | "grunt-contrib-watch": "~1.1.0", 68 | "grunt-css": "~0.5.4", 69 | "grunt-macreload": "*", 70 | "grunt-markdown": "~0.7.0", 71 | "mocha": "^6.1.3", 72 | "selenium-webdriver": "^4.20.0" 73 | }, 74 | "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" 75 | } 76 | -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /public/img/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/.gitignore -------------------------------------------------------------------------------- /public/img/DASHlogo_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/DASHlogo_white.png -------------------------------------------------------------------------------- /public/img/angularjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/angularjs.png -------------------------------------------------------------------------------- /public/img/icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/icons/copy.png -------------------------------------------------------------------------------- /public/img/icons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/icons/favicon.ico -------------------------------------------------------------------------------- /public/img/leveldb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/leveldb.png -------------------------------------------------------------------------------- /public/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/loading.gif -------------------------------------------------------------------------------- /public/img/nodejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/img/nodejs.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Insight 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 42 |
43 |
44 | 45 |
46 |
47 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /address 3 | Disallow: /api 4 | Disallow: /transaction 5 | -------------------------------------------------------------------------------- /public/sound/transaction.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dashpay/insight-ui/c8c1bb2b96b220a7b7cb546bfa22aabdede245ff/public/sound/transaction.mp3 -------------------------------------------------------------------------------- /public/src/js/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var defaultLanguage = localStorage.getItem('insight-language') || 'en'; 4 | var defaultCurrency = localStorage.getItem('insight-currency') || 'DASH'; 5 | 6 | angular.module('insight',[ 7 | 'ngAnimate', 8 | 'ngResource', 9 | 'ngRoute', 10 | 'ngProgress', 11 | 'ui.bootstrap', 12 | 'ui.route', 13 | 'monospaced.qrcode', 14 | 'gettext', 15 | 'angularMoment', 16 | 'insight.system', 17 | 'insight.socket', 18 | 'insight.hash', 19 | 'insight.blocks', 20 | 'insight.transactions', 21 | 'insight.address', 22 | 'insight.search', 23 | 'insight.status', 24 | 'insight.connection', 25 | 'insight.currency', 26 | 'insight.messages' 27 | ]); 28 | 29 | angular.module('insight.system', []); 30 | angular.module('insight.socket', []); 31 | angular.module('insight.hash', []); 32 | angular.module('insight.blocks', []); 33 | angular.module('insight.transactions', []); 34 | angular.module('insight.address', []); 35 | angular.module('insight.search', []); 36 | angular.module('insight.status', []); 37 | angular.module('insight.connection', []); 38 | angular.module('insight.currency', []); 39 | angular.module('insight.messages', []); 40 | -------------------------------------------------------------------------------- /public/src/js/config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //Setting up route 4 | angular.module('insight').config(function($routeProvider) { 5 | $routeProvider. 6 | when('/block/:blockHash', { 7 | templateUrl: 'views/block.html', 8 | title: 'Dash Block ' 9 | }). 10 | when('/block-index/:blockHeight', { 11 | controller: 'BlocksController', 12 | templateUrl: 'views/redirect.html' 13 | }). 14 | when('/tx/send', { 15 | templateUrl: 'views/transaction_sendraw.html', 16 | title: 'Broadcast Raw Transaction' 17 | }). 18 | when('/tx/:txId/:v_type?/:v_index?', { 19 | templateUrl: 'views/transaction.html', 20 | title: 'Dash Transaction ' 21 | }). 22 | when('/', { 23 | templateUrl: 'views/index.html', 24 | title: 'Home' 25 | }). 26 | when('/blocks', { 27 | templateUrl: 'views/block_list.html', 28 | title: 'Dash Blocks solved Today' 29 | }). 30 | when('/blocks-date/:blockDate/:startTimestamp?', { 31 | templateUrl: 'views/block_list.html', 32 | title: 'Dash Blocks solved ' 33 | }). 34 | when('/address/:addrStr', { 35 | templateUrl: 'views/address.html', 36 | title: 'Dash Address ' 37 | }). 38 | when('/status', { 39 | templateUrl: 'views/status.html', 40 | title: 'Status' 41 | }). 42 | when('/messages/verify', { 43 | templateUrl: 'views/messages_verify.html', 44 | title: 'Verify Message' 45 | }) 46 | .otherwise({ 47 | templateUrl: 'views/404.html', 48 | title: 'Error' 49 | }); 50 | }); 51 | 52 | //Setting HTML5 Location Mode 53 | angular.module('insight') 54 | .config(function($locationProvider) { 55 | $locationProvider.html5Mode(true); 56 | $locationProvider.hashPrefix('!'); 57 | }) 58 | .run(function($rootScope, $route, $location, $routeParams, $anchorScroll, ngProgressFactory, gettextCatalog, amMoment) { 59 | var ngProgress = ngProgressFactory.createInstance(); 60 | gettextCatalog.currentLanguage = defaultLanguage; 61 | amMoment.changeLocale(defaultLanguage); 62 | $rootScope.$on('$routeChangeStart', function() { 63 | ngProgress.start(); 64 | }); 65 | 66 | $rootScope.$on('$routeChangeSuccess', function() { 67 | ngProgress.complete(); 68 | 69 | //Change page title, based on Route information 70 | $rootScope.titleDetail = ''; 71 | $rootScope.title = $route.current.title; 72 | $rootScope.currentAddr = null; 73 | 74 | $location.hash($routeParams.scrollTo); 75 | $anchorScroll(); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /public/src/js/controllers/address.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.address').controller('AddressController', 4 | function($scope, $rootScope, $routeParams, $location, Global, Address, getSocket) { 5 | $scope.global = Global; 6 | 7 | var socket = getSocket($scope); 8 | var addrStr = $routeParams.addrStr; 9 | 10 | var _startSocket = function() { 11 | socket.on('dashd/addresstxid', function(data) { 12 | if (data.address === addrStr) { 13 | $rootScope.$broadcast('tx', data.txid); 14 | var base = document.querySelector('base'); 15 | var beep = new Audio(base.href + '/sound/transaction.mp3'); 16 | beep.play(); 17 | } 18 | }); 19 | socket.emit('subscribe', 'dashd/addresstxid', [addrStr]); 20 | }; 21 | 22 | var _stopSocket = function () { 23 | socket.emit('unsubscribe', 'dashd/addresstxid', [addrStr]); 24 | }; 25 | 26 | socket.on('connect', function() { 27 | _startSocket(); 28 | }); 29 | 30 | $scope.$on('$destroy', function(){ 31 | _stopSocket(); 32 | }); 33 | 34 | $scope.params = $routeParams; 35 | 36 | $scope.findOne = function() { 37 | $rootScope.currentAddr = $routeParams.addrStr; 38 | _startSocket(); 39 | 40 | Address.get({ 41 | addrStr: $routeParams.addrStr 42 | }, 43 | function(address) { 44 | $rootScope.titleDetail = address.addrStr.substring(0, 7) + '...'; 45 | $rootScope.flashMessage = null; 46 | $scope.address = address; 47 | }, 48 | function(e) { 49 | if (e.status === 400) { 50 | $rootScope.flashMessage = 'Invalid Address: ' + $routeParams.addrStr; 51 | } else if (e.status === 503) { 52 | $rootScope.flashMessage = 'Backend Error. ' + e.data; 53 | } else { 54 | $rootScope.flashMessage = 'Address Not Found'; 55 | } 56 | $location.path('/'); 57 | }); 58 | }; 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /public/src/js/controllers/blocks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.blocks').controller('BlocksController', 4 | function($scope, $rootScope, $routeParams, $location, Global, Block, Blocks, BlockByHeight) { 5 | $scope.global = Global; 6 | $scope.loading = false; 7 | 8 | if ($routeParams.blockHeight) { 9 | BlockByHeight.get({ 10 | blockHeight: $routeParams.blockHeight 11 | }, function(hash) { 12 | $location.path('/block/' + hash.blockHash); 13 | }, function() { 14 | $rootScope.flashMessage = 'Bad Request'; 15 | $location.path('/'); 16 | }); 17 | } 18 | 19 | var toUTCDate = function(date){ 20 | var _utc = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds()); 21 | return _utc; 22 | }; 23 | 24 | var millisToUTCDate = function(millis){ 25 | return toUTCDate(new Date(millis* 1000)); 26 | }; 27 | 28 | $scope.toUTCDate = toUTCDate; 29 | $scope.millisToUTCDate = millisToUTCDate; 30 | 31 | //Datepicker 32 | var _formatTimestamp = function (date) { 33 | var yyyy = date.getUTCFullYear().toString(); 34 | var mm = (date.getUTCMonth() + 1).toString(); // getMonth() is zero-based 35 | var dd = date.getUTCDate().toString(); 36 | 37 | return yyyy + '-' + (mm[1] ? mm : '0' + mm[0]) + '-' + (dd[1] ? dd : '0' + dd[0]); //padding 38 | }; 39 | 40 | $scope.$watch('dt', function(newValue, oldValue) { 41 | if (newValue !== oldValue) { 42 | $location.path('/blocks-date/' + _formatTimestamp(newValue)); 43 | } 44 | }); 45 | 46 | $scope.openCalendar = function($event) { 47 | $scope.opened = true; 48 | setTimeout(function(){ angular.element(document.querySelector('.uib-datepicker-popup'))[0].style.display = "block"; }, 100); 49 | }; 50 | 51 | $scope.humanSince = function(time) { 52 | var m = moment.unix(time).startOf('day'); 53 | var b = moment().startOf('day'); 54 | return m.max().from(b); 55 | }; 56 | 57 | 58 | $scope.list = function() { 59 | $scope.loading = true; 60 | 61 | if ($routeParams.blockDate) { 62 | $scope.detail = 'On ' + $routeParams.blockDate; 63 | } 64 | 65 | if ($routeParams.startTimestamp) { 66 | var d=new Date($routeParams.startTimestamp*1000); 67 | var m=d.getMinutes(); 68 | if (m<10) m = '0' + m; 69 | $scope.before = ' before ' + d.getHours() + ':' + m; 70 | } 71 | 72 | $rootScope.titleDetail = $scope.detail; 73 | 74 | Blocks.get({ 75 | blockDate: $routeParams.blockDate, 76 | startTimestamp: $routeParams.startTimestamp 77 | }, function(res) { 78 | $scope.loading = false; 79 | $scope.blocks = res.blocks; 80 | $scope.pagination = res.pagination; 81 | $scope.pagination.olderBlocks = $scope.pagination.moreTs; 82 | for (var key in $scope.blocks) { 83 | if($scope.blocks[key].time < $scope.pagination.olderBlocks){ 84 | $scope.pagination.olderBlocks = $scope.blocks[key].time; 85 | } 86 | } 87 | }); 88 | }; 89 | 90 | $scope.findOne = function() { 91 | $scope.loading = true; 92 | 93 | Block.get({ 94 | blockHash: $routeParams.blockHash 95 | }, function(block) { 96 | $rootScope.titleDetail = block.height; 97 | $rootScope.flashMessage = null; 98 | $scope.loading = false; 99 | $scope.block = block; 100 | }, function(e) { 101 | if (e.status === 400) { 102 | $rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId; 103 | } 104 | else if (e.status === 503) { 105 | $rootScope.flashMessage = 'Backend Error. ' + e.data; 106 | } 107 | else { 108 | $rootScope.flashMessage = 'Block Not Found'; 109 | } 110 | $location.path('/'); 111 | }); 112 | }; 113 | 114 | $scope.params = $routeParams; 115 | 116 | }); 117 | -------------------------------------------------------------------------------- /public/src/js/controllers/connection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.connection').controller('ConnectionController', 4 | function($scope, $window, Status, getSocket, PeerSync) { 5 | 6 | // Set initial values 7 | $scope.apiOnline = true; 8 | $scope.serverOnline = true; 9 | $scope.clienteOnline = true; 10 | 11 | var socket = getSocket($scope); 12 | 13 | // Check for the node server connection 14 | socket.on('connect', function() { 15 | $scope.serverOnline = true; 16 | socket.on('disconnect', function() { 17 | $scope.serverOnline = false; 18 | }); 19 | }); 20 | 21 | // Check for the api connection 22 | $scope.getConnStatus = function() { 23 | PeerSync.get({}, 24 | function(peer) { 25 | $scope.apiOnline = peer.connected; 26 | $scope.host = peer.host; 27 | $scope.port = peer.port; 28 | }, 29 | function() { 30 | $scope.apiOnline = false; 31 | }); 32 | }; 33 | 34 | socket.emit('subscribe', 'sync'); 35 | socket.on('status', function(sync) { 36 | $scope.sync = sync; 37 | $scope.apiOnline = (sync.status !== 'aborted' && sync.status !== 'error'); 38 | }); 39 | 40 | // Check for the client conneciton 41 | $window.addEventListener('offline', function() { 42 | $scope.$apply(function() { 43 | $scope.clienteOnline = false; 44 | }); 45 | }, true); 46 | 47 | $window.addEventListener('online', function() { 48 | $scope.$apply(function() { 49 | $scope.clienteOnline = true; 50 | }); 51 | }, true); 52 | 53 | }); 54 | -------------------------------------------------------------------------------- /public/src/js/controllers/currency.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.currency', ['ui.router','ui.bootstrap']).controller('CurrencyController', 4 | function($scope, $rootScope, Currency) { 5 | $rootScope.currency.symbol = defaultCurrency; 6 | 7 | var _roundFloat = function(x, n) { 8 | if(!parseInt(n, 10) || !parseFloat(x)) n = 0; 9 | 10 | return Math.round(x * Math.pow(10, n)) / Math.pow(10, n); 11 | }; 12 | 13 | $rootScope.currency.getConvertion = function(value) { 14 | value = value * 1; // Convert to number 15 | 16 | if (!isNaN(value) && typeof value !== 'undefined' && value !== null) { 17 | if (value === 0.00000000) return '0 ' + this.symbol; // fix value to show 18 | 19 | var response; 20 | 21 | if (this.symbol === 'USD') { 22 | response = _roundFloat((value * this.factor), 2); 23 | } else if (this.symbol === 'mDASH') { 24 | this.factor = 1000; 25 | response = _roundFloat((value * this.factor), 5); 26 | } else if (this.symbol === 'uDASH') { 27 | this.factor = 1000000; 28 | response = _roundFloat((value * this.factor), 2); 29 | } else { 30 | this.factor = 1; 31 | response = value; 32 | } 33 | // prevent sci notation 34 | if (response < 1e-7) response=response.toFixed(8); 35 | 36 | return response + ' ' + this.symbol; 37 | } 38 | 39 | return 'value error'; 40 | }; 41 | 42 | $scope.setCurrency = function(currency) { 43 | $rootScope.currency.symbol = currency; 44 | localStorage.setItem('insight-currency', currency); 45 | 46 | if (currency === 'USD') { 47 | Currency.get({}, function(res) { 48 | $rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp; 49 | }); 50 | } else if (currency === 'mDASH') { 51 | $rootScope.currency.factor = 1000; 52 | } else if (currency === 'uDASH') { 53 | $rootScope.currency.factor = 1000000; 54 | } else { 55 | $rootScope.currency.factor = 1; 56 | } 57 | }; 58 | 59 | // Get initial value 60 | Currency.get({}, function(res) { 61 | $rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp; 62 | }); 63 | 64 | }); 65 | -------------------------------------------------------------------------------- /public/src/js/controllers/footer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.system').controller('FooterController', 4 | function($scope, $route, $templateCache, gettextCatalog, amMoment, Version) { 5 | 6 | $scope.defaultLanguage = defaultLanguage; 7 | 8 | var _getVersion = function() { 9 | Version.get({}, 10 | function(res) { 11 | $scope.version = res.version; 12 | }); 13 | }; 14 | 15 | $scope.version = _getVersion(); 16 | 17 | $scope.availableLanguages = [{ 18 | name: 'Deutsch', 19 | isoCode: 'de_DE', 20 | }, { 21 | name: 'English', 22 | isoCode: 'en', 23 | }, { 24 | name: 'French', 25 | isoCode: 'fr_FR', 26 | }, { 27 | name: 'Spanish', 28 | isoCode: 'es', 29 | }, { 30 | name: 'Portuguese', 31 | isoCode: 'pt_BR', 32 | }, { 33 | name: 'Japanese', 34 | isoCode: 'ja', 35 | }, { 36 | name: 'Chinese', 37 | isoCode: 'zh', 38 | }]; 39 | 40 | $scope.setLanguage = function(isoCode) { 41 | gettextCatalog.currentLanguage = $scope.defaultLanguage = defaultLanguage = isoCode; 42 | amMoment.changeLocale(isoCode); 43 | localStorage.setItem('insight-language', isoCode); 44 | var currentPageTemplate = $route.current.templateUrl; 45 | $templateCache.remove(currentPageTemplate); 46 | $route.reload(); 47 | }; 48 | 49 | }); 50 | -------------------------------------------------------------------------------- /public/src/js/controllers/header.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.system').controller('HeaderController', 4 | function($scope, $rootScope, $uibModal, getSocket, Global, Block, Status) { 5 | $scope.global = Global; 6 | 7 | // This allow us to do crafted logic by the network, such can be seen in BlockHash.test(). 8 | $rootScope.network = 'testnet'; 9 | Status.get({}, 10 | function(status) { 11 | $rootScope.network = status.info && status.info.network || 'testnet'; 12 | }); 13 | 14 | $rootScope.currency = { 15 | factor: 1, 16 | bitstamp: 0, 17 | symbol: 'DASH' 18 | }; 19 | 20 | $scope.menu = [{ 21 | 'title': 'Blocks', 22 | 'link': 'blocks' 23 | }, { 24 | 'title': 'Status', 25 | 'link': 'status' 26 | }]; 27 | 28 | $scope.openScannerModal = function() { 29 | var modalInstance = $uibModal.open({ 30 | templateUrl: 'scannerModal.html', 31 | controller: 'ScannerController' 32 | }); 33 | }; 34 | 35 | var _getBlock = function(hash) { 36 | Block.get({ 37 | blockHash: hash 38 | }, function(res) { 39 | $scope.totalBlocks = res.height; 40 | }); 41 | }; 42 | 43 | var socket = getSocket($scope); 44 | socket.on('connect', function() { 45 | socket.emit('subscribe', 'inv'); 46 | 47 | socket.on('block', function(block) { 48 | var blockHash = block.toString(); 49 | _getBlock(blockHash); 50 | }); 51 | }); 52 | 53 | $rootScope.isCollapsed = true; 54 | }); 55 | -------------------------------------------------------------------------------- /public/src/js/controllers/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var TRANSACTION_DISPLAYED = 10; 4 | var BLOCKS_DISPLAYED = 5; 5 | 6 | angular.module('insight.system').controller('IndexController', 7 | function($scope, Global, getSocket, Blocks) { 8 | $scope.global = Global; 9 | 10 | var _getBlocks = function() { 11 | Blocks.get({ 12 | limit: BLOCKS_DISPLAYED 13 | }, function(res) { 14 | $scope.blocks = res.blocks; 15 | $scope.blocksLength = res.length; 16 | }); 17 | }; 18 | 19 | var socket = getSocket($scope); 20 | 21 | var _startSocket = function() { 22 | socket.emit('subscribe', 'inv'); 23 | socket.on('tx', function(tx) { 24 | $scope.txs.unshift(tx); 25 | if (parseInt($scope.txs.length, 10) >= parseInt(TRANSACTION_DISPLAYED, 10)) { 26 | $scope.txs = $scope.txs.splice(0, TRANSACTION_DISPLAYED); 27 | } 28 | }); 29 | 30 | socket.on('block', function() { 31 | _getBlocks(); 32 | }); 33 | }; 34 | 35 | socket.on('connect', function() { 36 | _startSocket(); 37 | }); 38 | 39 | 40 | 41 | $scope.humanSince = function(time) { 42 | var m = moment.unix(time); 43 | return m.max().fromNow(); 44 | }; 45 | 46 | $scope.index = function() { 47 | _getBlocks(); 48 | _startSocket(); 49 | }; 50 | 51 | $scope.txs = []; 52 | $scope.blocks = []; 53 | }); 54 | -------------------------------------------------------------------------------- /public/src/js/controllers/messages.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.messages').controller('VerifyMessageController', 4 | function($scope, $http) { 5 | $scope.message = { 6 | address: '', 7 | signature: '', 8 | message: '' 9 | }; 10 | $scope.verification = { 11 | status: 'unverified', // ready|loading|verified|error 12 | result: null, 13 | error: null, 14 | address: '' 15 | }; 16 | 17 | $scope.verifiable = function() { 18 | return ($scope.message.address 19 | && $scope.message.signature 20 | && $scope.message.message); 21 | }; 22 | $scope.verify = function() { 23 | $scope.verification.status = 'loading'; 24 | $scope.verification.address = $scope.message.address; 25 | 26 | var verify = function(data){ 27 | if(typeof(data.data.result) === 'boolean') { 28 | if(data.data.result){ 29 | $scope.verification.status = 'verified'; 30 | $scope.verification.result = data.data.result; 31 | }else{ 32 | $scope.verification.status = 'verified'; 33 | $scope.verification.result = false; 34 | } 35 | }else { 36 | // API returned 200 but result was not true or false 37 | $scope.verification.status = 'error'; 38 | $scope.verification.error = null; 39 | } 40 | }; 41 | 42 | $http.post(window.apiPrefix + '/messages/verify', $scope.message) 43 | .then(verify) 44 | .catch(function(data){ 45 | console.log('c',data); 46 | }); 47 | }; 48 | 49 | // Hide the verify status message on form change 50 | var unverify = function() { 51 | $scope.verification.status = 'unverified'; 52 | }; 53 | $scope.$watch('message.address', unverify); 54 | $scope.$watch('message.signature', unverify); 55 | $scope.$watch('message.message', unverify); 56 | }); 57 | -------------------------------------------------------------------------------- /public/src/js/controllers/scanner.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.system').controller('ScannerController', 4 | function($scope, $rootScope, $uibModalInstance, Global) { 5 | $scope.global = Global; 6 | 7 | // Detect mobile devices 8 | var isMobile = { 9 | Android: function() { 10 | return navigator.userAgent.match(/Android/i); 11 | }, 12 | BlackBerry: function() { 13 | return navigator.userAgent.match(/BlackBerry/i); 14 | }, 15 | iOS: function() { 16 | return navigator.userAgent.match(/iPhone|iPad|iPod/i); 17 | }, 18 | Opera: function() { 19 | return navigator.userAgent.match(/Opera Mini/i); 20 | }, 21 | Windows: function() { 22 | return navigator.userAgent.match(/IEMobile/i); 23 | }, 24 | any: function() { 25 | return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); 26 | } 27 | }; 28 | 29 | navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; 30 | window.URL = window.URL || window.webkitURL || window.mozURL || window.msURL; 31 | 32 | $scope.isMobile = isMobile.any(); 33 | $scope.scannerLoading = false; 34 | 35 | var $searchInput = angular.element(document.getElementById('search')), 36 | cameraInput, 37 | video, 38 | canvas, 39 | $video, 40 | context, 41 | localMediaStream; 42 | 43 | var _scan = function(evt) { 44 | if ($scope.isMobile) { 45 | $scope.scannerLoading = true; 46 | var files = evt.target.files; 47 | 48 | if (files.length === 1 && files[0].type.indexOf('image/') === 0) { 49 | var file = files[0]; 50 | 51 | var reader = new FileReader(); 52 | reader.onload = (function(theFile) { 53 | return function(e) { 54 | var mpImg = new MegaPixImage(file); 55 | mpImg.render(canvas, { maxWidth: 200, maxHeight: 200, orientation: 6 }); 56 | 57 | setTimeout(function() { 58 | qrcode.width = canvas.width; 59 | qrcode.height = canvas.height; 60 | qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height); 61 | 62 | try { 63 | //alert(JSON.stringify(qrcode.process(context))); 64 | qrcode.decode(); 65 | } catch (e) { 66 | alert(e); 67 | } 68 | }, 1500); 69 | }; 70 | })(file); 71 | 72 | // Read in the file as a data URL 73 | reader.readAsDataURL(file); 74 | } 75 | } else { 76 | if (localMediaStream) { 77 | context.drawImage(video, 0, 0, 300, 225); 78 | 79 | try { 80 | qrcode.decode(); 81 | } catch(e) { 82 | //qrcodeError(e); 83 | } 84 | } 85 | 86 | setTimeout(_scan, 500); 87 | } 88 | }; 89 | 90 | var _successCallback = function(stream) { 91 | video.src = (window.URL && window.URL.createObjectURL(stream)) || stream; 92 | localMediaStream = stream; 93 | video.play(); 94 | setTimeout(_scan, 1000); 95 | }; 96 | 97 | var _scanStop = function() { 98 | $scope.scannerLoading = false; 99 | $uibModalInstance.close(); 100 | if (!$scope.isMobile) { 101 | if (localMediaStream.stop) localMediaStream.stop(); 102 | localMediaStream = null; 103 | video.src = ''; 104 | } 105 | }; 106 | 107 | var _videoError = function(err) { 108 | console.log('Video Error: ' + JSON.stringify(err)); 109 | _scanStop(); 110 | }; 111 | 112 | qrcode.callback = function(data) { 113 | _scanStop(); 114 | 115 | var str = (data.indexOf('dash:') === 0) ? data.substring(8) : data; 116 | console.log('QR code detected: ' + str); 117 | $searchInput 118 | .val(str) 119 | .triggerHandler('change') 120 | .triggerHandler('submit'); 121 | }; 122 | 123 | $scope.cancel = function() { 124 | _scanStop(); 125 | }; 126 | 127 | $uibModalInstance.opened.then(function() { 128 | $rootScope.isCollapsed = true; 129 | 130 | // Start the scanner 131 | setTimeout(function() { 132 | canvas = document.getElementById('qr-canvas'); 133 | context = canvas.getContext('2d'); 134 | 135 | if ($scope.isMobile) { 136 | cameraInput = document.getElementById('qrcode-camera'); 137 | cameraInput.addEventListener('change', _scan, false); 138 | } else { 139 | video = document.getElementById('qrcode-scanner-video'); 140 | $video = angular.element(video); 141 | canvas.width = 300; 142 | canvas.height = 225; 143 | context.clearRect(0, 0, 300, 225); 144 | 145 | navigator.getUserMedia({video: true}, _successCallback, _videoError); 146 | } 147 | }, 500); 148 | }); 149 | }); 150 | -------------------------------------------------------------------------------- /public/src/js/controllers/search.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.search') 4 | .controller('SearchController', 5 | function ($scope, $rootScope, $routeParams, $location, $timeout, Global, Block, Transaction, Address, BlockHashValidator, TransactionHashValidator, AddressValidator, BlockByHeight) { 6 | $scope.global = Global; 7 | $scope.loading = false; 8 | 9 | var currentNetwork = $rootScope.network; 10 | 11 | var _badQuery = function () { 12 | $scope.badQuery = true; 13 | 14 | $timeout(function () { 15 | $scope.badQuery = false; 16 | }, 2000); 17 | }; 18 | 19 | var _resetSearch = function () { 20 | $scope.q = ''; 21 | $scope.loading = false; 22 | }; 23 | 24 | $scope.search = function () { 25 | var q = $scope.q; 26 | $scope.badQuery = false; 27 | $scope.loading = true; 28 | var isBlockHeight = isFinite(q); 29 | var isBlockHash = BlockHashValidator.test(q, currentNetwork); 30 | var isTransactionHash = TransactionHashValidator.test(q); 31 | var isAddress = AddressValidator.test(q); 32 | 33 | var badQueryLoadHandler = function () { 34 | $scope.loading = false; 35 | _badQuery(); 36 | }; 37 | 38 | var fetchAndRedirectTransactionSearch = function(){ 39 | return Transaction.get({ 40 | txId: q 41 | }, function () { 42 | _resetSearch(); 43 | $location.path('/tx/' + q); 44 | }, badQueryLoadHandler); 45 | }; 46 | 47 | var fetchAndRedirectBlockHeightSearch = function () { 48 | return BlockByHeight.get({ 49 | blockHeight: q 50 | }, function (hash) { 51 | _resetSearch(); 52 | $location.path('/block/' + hash.blockHash); 53 | }, badQueryLoadHandler); 54 | }; 55 | 56 | var fetchAndRedirectAddressSearch = function () { 57 | return Address.get({ 58 | addrStr: q 59 | }, function () { 60 | _resetSearch(); 61 | $location.path('address/' + q); 62 | }, badQueryLoadHandler); 63 | }; 64 | 65 | var fetchAndRedirectBlockSearch = function () { 66 | return Block.get({ 67 | blockHash: q 68 | }, function (res) { 69 | if(res.status === 404){ 70 | return fetchAndRedirectTransactionSearch(); 71 | } 72 | _resetSearch(); 73 | $location.path('/block/' + q); 74 | }, badQueryLoadHandler); 75 | }; 76 | 77 | if (isBlockHeight) { 78 | fetchAndRedirectBlockHeightSearch(); 79 | } else if (isAddress) { 80 | fetchAndRedirectAddressSearch(); 81 | } else if (isBlockHash) { 82 | fetchAndRedirectBlockSearch(); 83 | } else if (isTransactionHash) { 84 | fetchAndRedirectTransactionSearch(); 85 | } else { 86 | badQueryLoadHandler(); 87 | } 88 | }; 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /public/src/js/controllers/status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.status').controller('StatusController', 4 | function($scope, $routeParams, $location, Global, Status, Sync, getSocket) { 5 | $scope.global = Global; 6 | 7 | $scope.getStatus = function(q) { 8 | Status.get({ 9 | q: 'get' + q 10 | }, 11 | function(d) { 12 | $scope.loaded = 1; 13 | angular.extend($scope, d); 14 | }, 15 | function(e) { 16 | $scope.error = 'API ERROR: ' + e.data; 17 | }); 18 | }; 19 | 20 | $scope.humanSince = function(time) { 21 | var m = moment.unix(time / 1000); 22 | return m.max().fromNow(); 23 | }; 24 | 25 | var _onSyncUpdate = function(sync) { 26 | $scope.sync = sync; 27 | }; 28 | 29 | var socket = getSocket($scope); 30 | 31 | var _startSocket = function () { 32 | socket.emit('subscribe', 'sync'); 33 | socket.on('status', function(sync) { 34 | _onSyncUpdate(sync); 35 | }); 36 | }; 37 | 38 | socket.on('connect', function() { 39 | _startSocket(); 40 | }); 41 | 42 | 43 | $scope.getSync = function() { 44 | _startSocket(); 45 | Sync.get({}, 46 | function(sync) { 47 | _onSyncUpdate(sync); 48 | }, 49 | function(e) { 50 | var err = 'Could not get sync information' + e.toString(); 51 | $scope.sync = { 52 | error: err 53 | }; 54 | }); 55 | }; 56 | }); 57 | -------------------------------------------------------------------------------- /public/src/js/controllers/transactions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.transactions').controller('transactionsController', 4 | function($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress) { 5 | $scope.global = Global; 6 | $scope.loading = false; 7 | $scope.loadedBy = null; 8 | 9 | var pageNum = 0; 10 | var pagesTotal = 1; 11 | var COIN = 100000000; 12 | 13 | var _aggregateItems = function(items) { 14 | if (!items) return []; 15 | 16 | var l = items.length; 17 | 18 | var ret = []; 19 | var tmp = {}; 20 | var u = 0; 21 | 22 | for(var i=0; i < l; i++) { 23 | 24 | var notAddr = false; 25 | // non standard input 26 | if (items[i].scriptSig && !items[i].addr) { 27 | items[i].addr = 'Unparsed address [' + u++ + ']'; 28 | items[i].notAddr = true; 29 | notAddr = true; 30 | } 31 | 32 | // non standard output 33 | if (items[i].scriptPubKey && !items[i].scriptPubKey.addresses) { 34 | items[i].scriptPubKey.addresses = ['Unparsed address [' + u++ + ']']; 35 | items[i].notAddr = true; 36 | notAddr = true; 37 | } 38 | 39 | // multiple addr at output 40 | if (items[i].scriptPubKey && items[i].scriptPubKey.addresses.length > 1) { 41 | items[i].addr = items[i].scriptPubKey.addresses.join(','); 42 | ret.push(items[i]); 43 | continue; 44 | } 45 | 46 | var addr = items[i].addr || (items[i].scriptPubKey && items[i].scriptPubKey.addresses[0]); 47 | 48 | if (!tmp[addr]) { 49 | tmp[addr] = {}; 50 | tmp[addr].valueSat = 0; 51 | tmp[addr].count = 0; 52 | tmp[addr].addr = addr; 53 | tmp[addr].items = []; 54 | } 55 | tmp[addr].isSpent = items[i].spentTxId; 56 | 57 | tmp[addr].doubleSpentTxID = tmp[addr].doubleSpentTxID || items[i].doubleSpentTxID; 58 | tmp[addr].doubleSpentIndex = tmp[addr].doubleSpentIndex || items[i].doubleSpentIndex; 59 | tmp[addr].dbError = tmp[addr].dbError || items[i].dbError; 60 | tmp[addr].valueSat += Math.round(items[i].value * COIN); 61 | tmp[addr].items.push(items[i]); 62 | tmp[addr].notAddr = notAddr; 63 | 64 | if (items[i].unconfirmedInput) 65 | tmp[addr].unconfirmedInput = true; 66 | 67 | tmp[addr].count++; 68 | } 69 | 70 | angular.forEach(tmp, function(v) { 71 | v.value = v.value || parseInt(v.valueSat) / COIN; 72 | ret.push(v); 73 | }); 74 | return ret; 75 | }; 76 | 77 | var _processTX = function(tx) { 78 | if (tx.type !== 6 && tx.type !== 7) { 79 | tx.vinSimple = _aggregateItems(tx.vin); 80 | tx.voutSimple = _aggregateItems(tx.vout); 81 | } else { 82 | const title = [{ 83 | addr: tx.type === 6 ? 'QcTx' : 'MnHfTx', 84 | notAddr: true, 85 | count: 1, 86 | value: 0 87 | }]; 88 | tx.vinSimple = title; 89 | tx.voutSimple = title; 90 | } 91 | }; 92 | 93 | var _paginate = function(data) { 94 | $scope.loading = false; 95 | 96 | pagesTotal = data.pagesTotal; 97 | pageNum += 1; 98 | 99 | data.txs.forEach(function(tx) { 100 | _processTX(tx); 101 | $scope.txs.push(tx); 102 | }); 103 | }; 104 | 105 | var _byBlock = function() { 106 | TransactionsByBlock.get({ 107 | block: $routeParams.blockHash, 108 | pageNum: pageNum 109 | }, function(data) { 110 | _paginate(data); 111 | }); 112 | }; 113 | 114 | var _byAddress = function () { 115 | TransactionsByAddress.get({ 116 | address: $routeParams.addrStr, 117 | pageNum: pageNum 118 | }, function(data) { 119 | _paginate(data); 120 | }); 121 | }; 122 | 123 | var _findTx = function(txid) { 124 | Transaction.get({ 125 | txId: txid 126 | }, function(tx) { 127 | if(tx.txid){ 128 | $rootScope.titleDetail = tx.txid.substring(0,7) + '...'; 129 | } 130 | $rootScope.flashMessage = null; 131 | $scope.tx = tx; 132 | _processTX(tx); 133 | $scope.txs.unshift(tx); 134 | }, function(e) { 135 | // FIXME : Do we even enter here ? status 4** do not throw exceptions 136 | if (e.status === 400) { 137 | $rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId; 138 | } 139 | else if (e.status === 503) { 140 | $rootScope.flashMessage = 'Backend Error. ' + e.data; 141 | } 142 | else { 143 | $rootScope.flashMessage = 'Transaction Not Found'; 144 | } 145 | 146 | $location.path('/'); 147 | }); 148 | }; 149 | 150 | $scope.findThis = function() { 151 | _findTx($routeParams.txId); 152 | }; 153 | 154 | //Initial load 155 | $scope.load = function(from) { 156 | $scope.loadedBy = from; 157 | $scope.loadMore(); 158 | }; 159 | 160 | //Load more transactions for pagination 161 | $scope.loadMore = function() { 162 | if (pageNum < pagesTotal && !$scope.loading) { 163 | $scope.loading = true; 164 | 165 | if ($scope.loadedBy === 'address') { 166 | _byAddress(); 167 | } 168 | else { 169 | _byBlock(); 170 | } 171 | } 172 | }; 173 | 174 | // Highlighted txout 175 | if ($routeParams.v_type == '>' || $routeParams.v_type == '<') { 176 | $scope.from_vin = $routeParams.v_type == '<' ? true : false; 177 | $scope.from_vout = $routeParams.v_type == '>' ? true : false; 178 | $scope.v_index = parseInt($routeParams.v_index); 179 | $scope.itemsExpanded = true; 180 | } 181 | 182 | //Init without txs 183 | $scope.txs = []; 184 | 185 | $scope.$on('tx', function(event, txid) { 186 | _findTx(txid); 187 | }); 188 | 189 | }); 190 | 191 | angular.module('insight.transactions').controller('SendRawTransactionController', 192 | function($scope, $http) { 193 | $scope.transaction = ''; 194 | $scope.status = 'ready'; // ready|loading|sent|error 195 | $scope.txid = ''; 196 | $scope.error = null; 197 | 198 | $scope.formValid = function() { 199 | return !!$scope.transaction; 200 | }; 201 | $scope.send = function() { 202 | var postData = { 203 | rawtx: $scope.transaction 204 | }; 205 | $scope.status = 'loading'; 206 | $http.post(window.apiPrefix + '/tx/send', postData) 207 | .then(function(response) { 208 | const { data, status, headers, config } = response; 209 | if(typeof(data.txid) != 'string') { 210 | // API returned 200 but the format is not known 211 | $scope.status = 'error'; 212 | $scope.error = 'The transaction was sent but no transaction id was got back'; 213 | return; 214 | } 215 | 216 | $scope.status = 'sent'; 217 | $scope.txid = data.txid; 218 | }) 219 | .catch(function(response) { 220 | const { data, status, headers, config } = response; 221 | $scope.status = 'error'; 222 | if(data) { 223 | $scope.error = data; 224 | } else { 225 | $scope.error = "No error message given (connection error?)" 226 | } 227 | }); 228 | }; 229 | }); 230 | -------------------------------------------------------------------------------- /public/src/js/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight') 4 | .directive('scroll', function ($window) { 5 | return function(scope, element, attrs) { 6 | angular.element($window).bind('scroll', function() { 7 | if (this.pageYOffset >= 200) { 8 | scope.secondaryNavbar = true; 9 | } else { 10 | scope.secondaryNavbar = false; 11 | } 12 | scope.$apply(); 13 | }); 14 | }; 15 | }) 16 | .directive('whenScrolled', function($window) { 17 | return { 18 | restric: 'A', 19 | link: function(scope, elm, attr) { 20 | var pageHeight, clientHeight, scrollPos; 21 | $window = angular.element($window); 22 | 23 | var handler = function() { 24 | pageHeight = window.document.documentElement.scrollHeight; 25 | clientHeight = window.document.documentElement.clientHeight; 26 | scrollPos = window.pageYOffset; 27 | 28 | if (pageHeight - (scrollPos + clientHeight) === 0) { 29 | scope.$apply(attr.whenScrolled); 30 | } 31 | }; 32 | 33 | $window.on('scroll', handler); 34 | 35 | scope.$on('$destroy', function() { 36 | return $window.off('scroll', handler); 37 | }); 38 | } 39 | }; 40 | }) 41 | .directive('clipCopy', function() { 42 | 43 | return { 44 | restric: 'A', 45 | scope: { clipCopy: '=clipCopy' }, 46 | template: '', 47 | link: function(scope, elm) { 48 | elm.on('mousedown', function(event) { 49 | var text = scope.clipCopy; 50 | 51 | var textArea = document.createElement("textarea"); 52 | 53 | // Place in top-left corner of screen regardless of scroll position. 54 | textArea.style.position = 'fixed'; 55 | textArea.style.top = 0; 56 | textArea.style.left = 0; 57 | 58 | // Ensure it has a small width and height. Setting to 1px / 1em 59 | // doesn't work as this gives a negative w/h on some browsers. 60 | textArea.style.width = '2em'; 61 | textArea.style.height = '2em'; 62 | 63 | // We don't need padding, reducing the size if it does flash render. 64 | textArea.style.padding = 0; 65 | 66 | // Clean up any borders. 67 | textArea.style.border = 'none'; 68 | textArea.style.outline = 'none'; 69 | textArea.style.boxShadow = 'none'; 70 | 71 | // Avoid flash of white box if rendered for any reason. 72 | textArea.style.background = 'transparent'; 73 | 74 | var regex = //gi; 75 | textArea.value = text.replace(regex, "\n"); 76 | 77 | document.body.appendChild(textArea); 78 | 79 | textArea.select(); 80 | 81 | try { 82 | var successful = document.execCommand('copy'); 83 | document.execCommand("RemoveFormat"); 84 | var msg = successful ? 'successful' : 'unsuccessful'; 85 | //console.log('Copying text command was ' + msg); 86 | angular.element(elm[0].querySelector('.tooltip'))[0].style.display = "block"; 87 | angular.element(elm[0].querySelector('.tooltip'))[0].style.opacity = "1"; 88 | setTimeout(function(){ angular.element(elm[0].querySelector('.tooltip'))[0].style.opacity = "0"; angular.element(elm[0].querySelector('.tooltip'))[0].style.display = "none";}, 1000); 89 | 90 | } catch (err) { 91 | console.log('Oops, unable to copy'); 92 | } 93 | document.body.removeChild(textArea); 94 | }); 95 | } 96 | }; 97 | }) 98 | .directive('focus', function ($timeout) { 99 | return { 100 | scope: { 101 | trigger: '@focus' 102 | }, 103 | link: function (scope, element) { 104 | scope.$watch('trigger', function (value) { 105 | if (value === "true") { 106 | $timeout(function () { 107 | element[0].focus(); 108 | }); 109 | } 110 | }); 111 | } 112 | }; 113 | }); 114 | -------------------------------------------------------------------------------- /public/src/js/filters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight') 4 | .filter('startFrom', function() { 5 | return function(input, start) { 6 | start = +start; //parse to int 7 | return input.slice(start); 8 | } 9 | }) 10 | .filter('split', function() { 11 | return function(input, delimiter) { 12 | var delimiter = delimiter || ','; 13 | return input.split(delimiter); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /public/src/js/init.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.element(document).ready(function() { 4 | // Init the app 5 | // angular.bootstrap(document, ['insight']); 6 | }); 7 | -------------------------------------------------------------------------------- /public/src/js/ios-imagefile-megapixel/megapix-image.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Mega pixel image rendering library for iOS6 Safari 3 | * 4 | * Fixes iOS6 Safari's image file rendering issue for large size image (over mega-pixel), 5 | * which causes unexpected subsampling when drawing it in canvas. 6 | * By using this library, you can safely render the image with proper stretching. 7 | * 8 | * Copyright (c) 2012 Shinichi Tomita 9 | * Released under the MIT license 10 | */ 11 | (function() { 12 | 13 | /** 14 | * Detect subsampling in loaded image. 15 | * In iOS, larger images than 2M pixels may be subsampled in rendering. 16 | */ 17 | function detectSubsampling(img) { 18 | var iw = img.naturalWidth, ih = img.naturalHeight; 19 | if (iw * ih > 1024 * 1024) { // subsampling may happen over megapixel image 20 | var canvas = document.createElement('canvas'); 21 | canvas.width = canvas.height = 1; 22 | var ctx = canvas.getContext('2d'); 23 | ctx.drawImage(img, -iw + 1, 0); 24 | // subsampled image becomes half smaller in rendering size. 25 | // check alpha channel value to confirm image is covering edge pixel or not. 26 | // if alpha value is 0 image is not covering, hence subsampled. 27 | return ctx.getImageData(0, 0, 1, 1).data[3] === 0; 28 | } else { 29 | return false; 30 | } 31 | } 32 | 33 | /** 34 | * Detecting vertical squash in loaded image. 35 | * Fixes a bug which squash image vertically while drawing into canvas for some images. 36 | */ 37 | function detectVerticalSquash(img, iw, ih) { 38 | var canvas = document.createElement('canvas'); 39 | canvas.width = 1; 40 | canvas.height = ih; 41 | var ctx = canvas.getContext('2d'); 42 | ctx.drawImage(img, 0, 0); 43 | var data = ctx.getImageData(0, 0, 1, ih).data; 44 | // search image edge pixel position in case it is squashed vertically. 45 | var sy = 0; 46 | var ey = ih; 47 | var py = ih; 48 | while (py > sy) { 49 | var alpha = data[(py - 1) * 4 + 3]; 50 | if (alpha === 0) { 51 | ey = py; 52 | } else { 53 | sy = py; 54 | } 55 | py = (ey + sy) >> 1; 56 | } 57 | var ratio = (py / ih); 58 | return (ratio===0)?1:ratio; 59 | } 60 | 61 | /** 62 | * Rendering image element (with resizing) and get its data URL 63 | */ 64 | function renderImageToDataURL(img, options, doSquash) { 65 | var canvas = document.createElement('canvas'); 66 | renderImageToCanvas(img, canvas, options, doSquash); 67 | return canvas.toDataURL("image/jpeg", options.quality || 0.8); 68 | } 69 | 70 | /** 71 | * Rendering image element (with resizing) into the canvas element 72 | */ 73 | function renderImageToCanvas(img, canvas, options, doSquash) { 74 | var iw = img.naturalWidth, ih = img.naturalHeight; 75 | var width = options.width, height = options.height; 76 | var ctx = canvas.getContext('2d'); 77 | ctx.save(); 78 | transformCoordinate(canvas, width, height, options.orientation); 79 | var subsampled = detectSubsampling(img); 80 | if (subsampled) { 81 | iw /= 2; 82 | ih /= 2; 83 | } 84 | var d = 1024; // size of tiling canvas 85 | var tmpCanvas = document.createElement('canvas'); 86 | tmpCanvas.width = tmpCanvas.height = d; 87 | var tmpCtx = tmpCanvas.getContext('2d'); 88 | var vertSquashRatio = doSquash ? detectVerticalSquash(img, iw, ih) : 1; 89 | var dw = Math.ceil(d * width / iw); 90 | var dh = Math.ceil(d * height / ih / vertSquashRatio); 91 | var sy = 0; 92 | var dy = 0; 93 | while (sy < ih) { 94 | var sx = 0; 95 | var dx = 0; 96 | while (sx < iw) { 97 | tmpCtx.clearRect(0, 0, d, d); 98 | tmpCtx.drawImage(img, -sx, -sy); 99 | ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); 100 | sx += d; 101 | dx += dw; 102 | } 103 | sy += d; 104 | dy += dh; 105 | } 106 | ctx.restore(); 107 | tmpCanvas = tmpCtx = null; 108 | } 109 | 110 | /** 111 | * Transform canvas coordination according to specified frame size and orientation 112 | * Orientation value is from EXIF tag 113 | */ 114 | function transformCoordinate(canvas, width, height, orientation) { 115 | switch (orientation) { 116 | case 5: 117 | case 6: 118 | case 7: 119 | case 8: 120 | canvas.width = height; 121 | canvas.height = width; 122 | break; 123 | default: 124 | canvas.width = width; 125 | canvas.height = height; 126 | } 127 | var ctx = canvas.getContext('2d'); 128 | switch (orientation) { 129 | case 2: 130 | // horizontal flip 131 | ctx.translate(width, 0); 132 | ctx.scale(-1, 1); 133 | break; 134 | case 3: 135 | // 180 rotate left 136 | ctx.translate(width, height); 137 | ctx.rotate(Math.PI); 138 | break; 139 | case 4: 140 | // vertical flip 141 | ctx.translate(0, height); 142 | ctx.scale(1, -1); 143 | break; 144 | case 5: 145 | // vertical flip + 90 rotate right 146 | ctx.rotate(0.5 * Math.PI); 147 | ctx.scale(1, -1); 148 | break; 149 | case 6: 150 | // 90 rotate right 151 | ctx.rotate(0.5 * Math.PI); 152 | ctx.translate(0, -height); 153 | break; 154 | case 7: 155 | // horizontal flip + 90 rotate right 156 | ctx.rotate(0.5 * Math.PI); 157 | ctx.translate(width, -height); 158 | ctx.scale(-1, 1); 159 | break; 160 | case 8: 161 | // 90 rotate left 162 | ctx.rotate(-0.5 * Math.PI); 163 | ctx.translate(-width, 0); 164 | break; 165 | default: 166 | break; 167 | } 168 | } 169 | 170 | 171 | /** 172 | * MegaPixImage class 173 | */ 174 | function MegaPixImage(srcImage) { 175 | if (window.Blob && srcImage instanceof Blob) { 176 | var img = new Image(); 177 | var URL = window.URL && window.URL.createObjectURL ? window.URL : 178 | window.webkitURL && window.webkitURL.createObjectURL ? window.webkitURL : 179 | null; 180 | if (!URL) { throw Error("No createObjectURL function found to create blob url"); } 181 | img.src = URL.createObjectURL(srcImage); 182 | this.blob = srcImage; 183 | srcImage = img; 184 | } 185 | if (!srcImage.naturalWidth && !srcImage.naturalHeight) { 186 | var _this = this; 187 | srcImage.onload = function() { 188 | var listeners = _this.imageLoadListeners; 189 | if (listeners) { 190 | _this.imageLoadListeners = null; 191 | for (var i=0, len=listeners.length; i maxWidth) { 224 | width = maxWidth; 225 | height = (imgHeight * width / imgWidth) << 0; 226 | } 227 | if (maxHeight && height > maxHeight) { 228 | height = maxHeight; 229 | width = (imgWidth * height / imgHeight) << 0; 230 | } 231 | var opt = { width : width, height : height }; 232 | for (var k in options) opt[k] = options[k]; 233 | 234 | var tagName = target.tagName.toLowerCase(); 235 | if (tagName === 'img') { 236 | target.src = renderImageToDataURL(this.srcImage, opt, doSquash); 237 | } else if (tagName === 'canvas') { 238 | renderImageToCanvas(this.srcImage, target, opt, doSquash); 239 | } 240 | if (typeof this.onrender === 'function') { 241 | this.onrender(target); 242 | } 243 | }; 244 | 245 | /** 246 | * Export class to global 247 | */ 248 | if (typeof define === 'function' && define.amd) { 249 | define([], function() { return MegaPixImage; }); // for AMD loader 250 | } else { 251 | this.MegaPixImage = MegaPixImage; 252 | } 253 | 254 | })(); 255 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/alignpat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function AlignmentPattern(posX, posY, estimatedModuleSize) 27 | { 28 | this.x=posX; 29 | this.y=posY; 30 | this.count = 1; 31 | this.estimatedModuleSize = estimatedModuleSize; 32 | 33 | this.__defineGetter__("EstimatedModuleSize", function() 34 | { 35 | return this.estimatedModuleSize; 36 | }); 37 | this.__defineGetter__("Count", function() 38 | { 39 | return this.count; 40 | }); 41 | this.__defineGetter__("X", function() 42 | { 43 | return Math.floor(this.x); 44 | }); 45 | this.__defineGetter__("Y", function() 46 | { 47 | return Math.floor(this.y); 48 | }); 49 | this.incrementCount = function() 50 | { 51 | this.count++; 52 | } 53 | this.aboutEquals=function( moduleSize, i, j) 54 | { 55 | if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize) 56 | { 57 | var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize); 58 | return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0; 59 | } 60 | return false; 61 | } 62 | 63 | } 64 | 65 | function AlignmentPatternFinder( image, startX, startY, width, height, moduleSize, resultPointCallback) 66 | { 67 | this.image = image; 68 | this.possibleCenters = new Array(); 69 | this.startX = startX; 70 | this.startY = startY; 71 | this.width = width; 72 | this.height = height; 73 | this.moduleSize = moduleSize; 74 | this.crossCheckStateCount = new Array(0,0,0); 75 | this.resultPointCallback = resultPointCallback; 76 | 77 | this.centerFromEnd=function(stateCount, end) 78 | { 79 | return (end - stateCount[2]) - stateCount[1] / 2.0; 80 | } 81 | this.foundPatternCross = function(stateCount) 82 | { 83 | var moduleSize = this.moduleSize; 84 | var maxVariance = moduleSize / 2.0; 85 | for (var i = 0; i < 3; i++) 86 | { 87 | if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) 88 | { 89 | return false; 90 | } 91 | } 92 | return true; 93 | } 94 | 95 | this.crossCheckVertical=function( startI, centerJ, maxCount, originalStateCountTotal) 96 | { 97 | var image = this.image; 98 | 99 | var maxI = qrcode.height; 100 | var stateCount = this.crossCheckStateCount; 101 | stateCount[0] = 0; 102 | stateCount[1] = 0; 103 | stateCount[2] = 0; 104 | 105 | // Start counting up from center 106 | var i = startI; 107 | while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[1] <= maxCount) 108 | { 109 | stateCount[1]++; 110 | i--; 111 | } 112 | // If already too many modules in this state or ran off the edge: 113 | if (i < 0 || stateCount[1] > maxCount) 114 | { 115 | return NaN; 116 | } 117 | while (i >= 0 && !image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount) 118 | { 119 | stateCount[0]++; 120 | i--; 121 | } 122 | if (stateCount[0] > maxCount) 123 | { 124 | return NaN; 125 | } 126 | 127 | // Now also count down from center 128 | i = startI + 1; 129 | while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[1] <= maxCount) 130 | { 131 | stateCount[1]++; 132 | i++; 133 | } 134 | if (i == maxI || stateCount[1] > maxCount) 135 | { 136 | return NaN; 137 | } 138 | while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[2] <= maxCount) 139 | { 140 | stateCount[2]++; 141 | i++; 142 | } 143 | if (stateCount[2] > maxCount) 144 | { 145 | return NaN; 146 | } 147 | 148 | var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; 149 | if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) 150 | { 151 | return NaN; 152 | } 153 | 154 | return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN; 155 | } 156 | 157 | this.handlePossibleCenter=function( stateCount, i, j) 158 | { 159 | var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; 160 | var centerJ = this.centerFromEnd(stateCount, j); 161 | var centerI = this.crossCheckVertical(i, Math.floor (centerJ), 2 * stateCount[1], stateCountTotal); 162 | if (!isNaN(centerI)) 163 | { 164 | var estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0; 165 | var max = this.possibleCenters.length; 166 | for (var index = 0; index < max; index++) 167 | { 168 | var center = this.possibleCenters[index]; 169 | // Look for about the same center and module size: 170 | if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) 171 | { 172 | return new AlignmentPattern(centerJ, centerI, estimatedModuleSize); 173 | } 174 | } 175 | // Hadn't found this before; save it 176 | var point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); 177 | this.possibleCenters.push(point); 178 | if (this.resultPointCallback != null) 179 | { 180 | this.resultPointCallback.foundPossibleResultPoint(point); 181 | } 182 | } 183 | return null; 184 | } 185 | 186 | this.find = function() 187 | { 188 | var startX = this.startX; 189 | var height = this.height; 190 | var maxJ = startX + width; 191 | var middleI = startY + (height >> 1); 192 | // We are looking for black/white/black modules in 1:1:1 ratio; 193 | // this tracks the number of black/white/black modules seen so far 194 | var stateCount = new Array(0,0,0); 195 | for (var iGen = 0; iGen < height; iGen++) 196 | { 197 | // Search from middle outwards 198 | var i = middleI + ((iGen & 0x01) == 0?((iGen + 1) >> 1):- ((iGen + 1) >> 1)); 199 | stateCount[0] = 0; 200 | stateCount[1] = 0; 201 | stateCount[2] = 0; 202 | var j = startX; 203 | // Burn off leading white pixels before anything else; if we start in the middle of 204 | // a white run, it doesn't make sense to count its length, since we don't know if the 205 | // white run continued to the left of the start point 206 | while (j < maxJ && !image[j + qrcode.width* i]) 207 | { 208 | j++; 209 | } 210 | var currentState = 0; 211 | while (j < maxJ) 212 | { 213 | if (image[j + i*qrcode.width]) 214 | { 215 | // Black pixel 216 | if (currentState == 1) 217 | { 218 | // Counting black pixels 219 | stateCount[currentState]++; 220 | } 221 | else 222 | { 223 | // Counting white pixels 224 | if (currentState == 2) 225 | { 226 | // A winner? 227 | if (this.foundPatternCross(stateCount)) 228 | { 229 | // Yes 230 | var confirmed = this.handlePossibleCenter(stateCount, i, j); 231 | if (confirmed != null) 232 | { 233 | return confirmed; 234 | } 235 | } 236 | stateCount[0] = stateCount[2]; 237 | stateCount[1] = 1; 238 | stateCount[2] = 0; 239 | currentState = 1; 240 | } 241 | else 242 | { 243 | stateCount[++currentState]++; 244 | } 245 | } 246 | } 247 | else 248 | { 249 | // White pixel 250 | if (currentState == 1) 251 | { 252 | // Counting black pixels 253 | currentState++; 254 | } 255 | stateCount[currentState]++; 256 | } 257 | j++; 258 | } 259 | if (this.foundPatternCross(stateCount)) 260 | { 261 | var confirmed = this.handlePossibleCenter(stateCount, i, maxJ); 262 | if (confirmed != null) 263 | { 264 | return confirmed; 265 | } 266 | } 267 | } 268 | 269 | // Hmm, nothing we saw was observed and confirmed twice. If we had 270 | // any guess at all, return it. 271 | if (!(this.possibleCenters.length == 0)) 272 | { 273 | return this.possibleCenters[0]; 274 | } 275 | 276 | throw "Couldn't find enough alignment patterns"; 277 | } 278 | 279 | } -------------------------------------------------------------------------------- /public/src/js/jsqrcode/bitmat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function BitMatrix( width, height) 27 | { 28 | if(!height) 29 | height=width; 30 | if (width < 1 || height < 1) 31 | { 32 | throw "Both dimensions must be greater than 0"; 33 | } 34 | this.width = width; 35 | this.height = height; 36 | var rowSize = width >> 5; 37 | if ((width & 0x1f) != 0) 38 | { 39 | rowSize++; 40 | } 41 | this.rowSize = rowSize; 42 | this.bits = new Array(rowSize * height); 43 | for(var i=0;i> 5); 66 | return ((URShift(this.bits[offset], (x & 0x1f))) & 1) != 0; 67 | } 68 | this.set_Renamed=function( x, y) 69 | { 70 | var offset = y * this.rowSize + (x >> 5); 71 | this.bits[offset] |= 1 << (x & 0x1f); 72 | } 73 | this.flip=function( x, y) 74 | { 75 | var offset = y * this.rowSize + (x >> 5); 76 | this.bits[offset] ^= 1 << (x & 0x1f); 77 | } 78 | this.clear=function() 79 | { 80 | var max = this.bits.length; 81 | for (var i = 0; i < max; i++) 82 | { 83 | this.bits[i] = 0; 84 | } 85 | } 86 | this.setRegion=function( left, top, width, height) 87 | { 88 | if (top < 0 || left < 0) 89 | { 90 | throw "Left and top must be nonnegative"; 91 | } 92 | if (height < 1 || width < 1) 93 | { 94 | throw "Height and width must be at least 1"; 95 | } 96 | var right = left + width; 97 | var bottom = top + height; 98 | if (bottom > this.height || right > this.width) 99 | { 100 | throw "The region must fit inside the matrix"; 101 | } 102 | for (var y = top; y < bottom; y++) 103 | { 104 | var offset = y * this.rowSize; 105 | for (var x = left; x < right; x++) 106 | { 107 | this.bits[offset + (x >> 5)] |= 1 << (x & 0x1f); 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /public/src/js/jsqrcode/bmparser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function BitMatrixParser(bitMatrix) 27 | { 28 | var dimension = bitMatrix.Dimension; 29 | if (dimension < 21 || (dimension & 0x03) != 1) 30 | { 31 | throw "Error BitMatrixParser"; 32 | } 33 | this.bitMatrix = bitMatrix; 34 | this.parsedVersion = null; 35 | this.parsedFormatInfo = null; 36 | 37 | this.copyBit=function( i, j, versionBits) 38 | { 39 | return this.bitMatrix.get_Renamed(i, j)?(versionBits << 1) | 0x1:versionBits << 1; 40 | } 41 | 42 | this.readFormatInformation=function() 43 | { 44 | if (this.parsedFormatInfo != null) 45 | { 46 | return this.parsedFormatInfo; 47 | } 48 | 49 | // Read top-left format info bits 50 | var formatInfoBits = 0; 51 | for (var i = 0; i < 6; i++) 52 | { 53 | formatInfoBits = this.copyBit(i, 8, formatInfoBits); 54 | } 55 | // .. and skip a bit in the timing pattern ... 56 | formatInfoBits = this.copyBit(7, 8, formatInfoBits); 57 | formatInfoBits = this.copyBit(8, 8, formatInfoBits); 58 | formatInfoBits = this.copyBit(8, 7, formatInfoBits); 59 | // .. and skip a bit in the timing pattern ... 60 | for (var j = 5; j >= 0; j--) 61 | { 62 | formatInfoBits = this.copyBit(8, j, formatInfoBits); 63 | } 64 | 65 | this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); 66 | if (this.parsedFormatInfo != null) 67 | { 68 | return this.parsedFormatInfo; 69 | } 70 | 71 | // Hmm, failed. Try the top-right/bottom-left pattern 72 | var dimension = this.bitMatrix.Dimension; 73 | formatInfoBits = 0; 74 | var iMin = dimension - 8; 75 | for (var i = dimension - 1; i >= iMin; i--) 76 | { 77 | formatInfoBits = this.copyBit(i, 8, formatInfoBits); 78 | } 79 | for (var j = dimension - 7; j < dimension; j++) 80 | { 81 | formatInfoBits = this.copyBit(8, j, formatInfoBits); 82 | } 83 | 84 | this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); 85 | if (this.parsedFormatInfo != null) 86 | { 87 | return this.parsedFormatInfo; 88 | } 89 | throw "Error readFormatInformation"; 90 | } 91 | this.readVersion=function() 92 | { 93 | 94 | if (this.parsedVersion != null) 95 | { 96 | return this.parsedVersion; 97 | } 98 | 99 | var dimension = this.bitMatrix.Dimension; 100 | 101 | var provisionalVersion = (dimension - 17) >> 2; 102 | if (provisionalVersion <= 6) 103 | { 104 | return Version.getVersionForNumber(provisionalVersion); 105 | } 106 | 107 | // Read top-right version info: 3 wide by 6 tall 108 | var versionBits = 0; 109 | var ijMin = dimension - 11; 110 | for (var j = 5; j >= 0; j--) 111 | { 112 | for (var i = dimension - 9; i >= ijMin; i--) 113 | { 114 | versionBits = this.copyBit(i, j, versionBits); 115 | } 116 | } 117 | 118 | this.parsedVersion = Version.decodeVersionInformation(versionBits); 119 | if (this.parsedVersion != null && this.parsedVersion.DimensionForVersion == dimension) 120 | { 121 | return this.parsedVersion; 122 | } 123 | 124 | // Hmm, failed. Try bottom left: 6 wide by 3 tall 125 | versionBits = 0; 126 | for (var i = 5; i >= 0; i--) 127 | { 128 | for (var j = dimension - 9; j >= ijMin; j--) 129 | { 130 | versionBits = this.copyBit(i, j, versionBits); 131 | } 132 | } 133 | 134 | this.parsedVersion = Version.decodeVersionInformation(versionBits); 135 | if (this.parsedVersion != null && this.parsedVersion.DimensionForVersion == dimension) 136 | { 137 | return this.parsedVersion; 138 | } 139 | throw "Error readVersion"; 140 | } 141 | this.readCodewords=function() 142 | { 143 | 144 | var formatInfo = this.readFormatInformation(); 145 | var version = this.readVersion(); 146 | 147 | // Get the data mask for the format used in this QR Code. This will exclude 148 | // some bits from reading as we wind through the bit matrix. 149 | var dataMask = DataMask.forReference( formatInfo.DataMask); 150 | var dimension = this.bitMatrix.Dimension; 151 | dataMask.unmaskBitMatrix(this.bitMatrix, dimension); 152 | 153 | var functionPattern = version.buildFunctionPattern(); 154 | 155 | var readingUp = true; 156 | var result = new Array(version.TotalCodewords); 157 | var resultOffset = 0; 158 | var currentByte = 0; 159 | var bitsRead = 0; 160 | // Read columns in pairs, from right to left 161 | for (var j = dimension - 1; j > 0; j -= 2) 162 | { 163 | if (j == 6) 164 | { 165 | // Skip whole column with vertical alignment pattern; 166 | // saves time and makes the other code proceed more cleanly 167 | j--; 168 | } 169 | // Read alternatingly from bottom to top then top to bottom 170 | for (var count = 0; count < dimension; count++) 171 | { 172 | var i = readingUp?dimension - 1 - count:count; 173 | for (var col = 0; col < 2; col++) 174 | { 175 | // Ignore bits covered by the function pattern 176 | if (!functionPattern.get_Renamed(j - col, i)) 177 | { 178 | // Read a bit 179 | bitsRead++; 180 | currentByte <<= 1; 181 | if (this.bitMatrix.get_Renamed(j - col, i)) 182 | { 183 | currentByte |= 1; 184 | } 185 | // If we've made a whole byte, save it off 186 | if (bitsRead == 8) 187 | { 188 | result[resultOffset++] = currentByte; 189 | bitsRead = 0; 190 | currentByte = 0; 191 | } 192 | } 193 | } 194 | } 195 | readingUp ^= true; // readingUp = !readingUp; // switch directions 196 | } 197 | if (resultOffset != version.TotalCodewords) 198 | { 199 | throw "Error readCodewords"; 200 | } 201 | return result; 202 | } 203 | } -------------------------------------------------------------------------------- /public/src/js/jsqrcode/datablock.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function DataBlock(numDataCodewords, codewords) 27 | { 28 | this.numDataCodewords = numDataCodewords; 29 | this.codewords = codewords; 30 | 31 | this.__defineGetter__("NumDataCodewords", function() 32 | { 33 | return this.numDataCodewords; 34 | }); 35 | this.__defineGetter__("Codewords", function() 36 | { 37 | return this.codewords; 38 | }); 39 | } 40 | 41 | DataBlock.getDataBlocks=function(rawCodewords, version, ecLevel) 42 | { 43 | 44 | if (rawCodewords.length != version.TotalCodewords) 45 | { 46 | throw "ArgumentException"; 47 | } 48 | 49 | // Figure out the number and size of data blocks used by this version and 50 | // error correction level 51 | var ecBlocks = version.getECBlocksForLevel(ecLevel); 52 | 53 | // First count the total number of data blocks 54 | var totalBlocks = 0; 55 | var ecBlockArray = ecBlocks.getECBlocks(); 56 | for (var i = 0; i < ecBlockArray.length; i++) 57 | { 58 | totalBlocks += ecBlockArray[i].Count; 59 | } 60 | 61 | // Now establish DataBlocks of the appropriate size and number of data codewords 62 | var result = new Array(totalBlocks); 63 | var numResultBlocks = 0; 64 | for (var j = 0; j < ecBlockArray.length; j++) 65 | { 66 | var ecBlock = ecBlockArray[j]; 67 | for (var i = 0; i < ecBlock.Count; i++) 68 | { 69 | var numDataCodewords = ecBlock.DataCodewords; 70 | var numBlockCodewords = ecBlocks.ECCodewordsPerBlock + numDataCodewords; 71 | result[numResultBlocks++] = new DataBlock(numDataCodewords, new Array(numBlockCodewords)); 72 | } 73 | } 74 | 75 | // All blocks have the same amount of data, except that the last n 76 | // (where n may be 0) have 1 more byte. Figure out where these start. 77 | var shorterBlocksTotalCodewords = result[0].codewords.length; 78 | var longerBlocksStartAt = result.length - 1; 79 | while (longerBlocksStartAt >= 0) 80 | { 81 | var numCodewords = result[longerBlocksStartAt].codewords.length; 82 | if (numCodewords == shorterBlocksTotalCodewords) 83 | { 84 | break; 85 | } 86 | longerBlocksStartAt--; 87 | } 88 | longerBlocksStartAt++; 89 | 90 | var shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock; 91 | // The last elements of result may be 1 element longer; 92 | // first fill out as many elements as all of them have 93 | var rawCodewordsOffset = 0; 94 | for (var i = 0; i < shorterBlocksNumDataCodewords; i++) 95 | { 96 | for (var j = 0; j < numResultBlocks; j++) 97 | { 98 | result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; 99 | } 100 | } 101 | // Fill out the last data block in the longer ones 102 | for (var j = longerBlocksStartAt; j < numResultBlocks; j++) 103 | { 104 | result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; 105 | } 106 | // Now add in error correction blocks 107 | var max = result[0].codewords.length; 108 | for (var i = shorterBlocksNumDataCodewords; i < max; i++) 109 | { 110 | for (var j = 0; j < numResultBlocks; j++) 111 | { 112 | var iOffset = j < longerBlocksStartAt?i:i + 1; 113 | result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; 114 | } 115 | } 116 | return result; 117 | } 118 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/datamask.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | DataMask = {}; 27 | 28 | DataMask.forReference = function(reference) 29 | { 30 | if (reference < 0 || reference > 7) 31 | { 32 | throw "System.ArgumentException"; 33 | } 34 | return DataMask.DATA_MASKS[reference]; 35 | } 36 | 37 | function DataMask000() 38 | { 39 | this.unmaskBitMatrix=function(bits, dimension) 40 | { 41 | for (var i = 0; i < dimension; i++) 42 | { 43 | for (var j = 0; j < dimension; j++) 44 | { 45 | if (this.isMasked(i, j)) 46 | { 47 | bits.flip(j, i); 48 | } 49 | } 50 | } 51 | } 52 | this.isMasked=function( i, j) 53 | { 54 | return ((i + j) & 0x01) == 0; 55 | } 56 | } 57 | 58 | function DataMask001() 59 | { 60 | this.unmaskBitMatrix=function(bits, dimension) 61 | { 62 | for (var i = 0; i < dimension; i++) 63 | { 64 | for (var j = 0; j < dimension; j++) 65 | { 66 | if (this.isMasked(i, j)) 67 | { 68 | bits.flip(j, i); 69 | } 70 | } 71 | } 72 | } 73 | this.isMasked=function( i, j) 74 | { 75 | return (i & 0x01) == 0; 76 | } 77 | } 78 | 79 | function DataMask010() 80 | { 81 | this.unmaskBitMatrix=function(bits, dimension) 82 | { 83 | for (var i = 0; i < dimension; i++) 84 | { 85 | for (var j = 0; j < dimension; j++) 86 | { 87 | if (this.isMasked(i, j)) 88 | { 89 | bits.flip(j, i); 90 | } 91 | } 92 | } 93 | } 94 | this.isMasked=function( i, j) 95 | { 96 | return j % 3 == 0; 97 | } 98 | } 99 | 100 | function DataMask011() 101 | { 102 | this.unmaskBitMatrix=function(bits, dimension) 103 | { 104 | for (var i = 0; i < dimension; i++) 105 | { 106 | for (var j = 0; j < dimension; j++) 107 | { 108 | if (this.isMasked(i, j)) 109 | { 110 | bits.flip(j, i); 111 | } 112 | } 113 | } 114 | } 115 | this.isMasked=function( i, j) 116 | { 117 | return (i + j) % 3 == 0; 118 | } 119 | } 120 | 121 | function DataMask100() 122 | { 123 | this.unmaskBitMatrix=function(bits, dimension) 124 | { 125 | for (var i = 0; i < dimension; i++) 126 | { 127 | for (var j = 0; j < dimension; j++) 128 | { 129 | if (this.isMasked(i, j)) 130 | { 131 | bits.flip(j, i); 132 | } 133 | } 134 | } 135 | } 136 | this.isMasked=function( i, j) 137 | { 138 | return (((URShift(i, 1)) + (j / 3)) & 0x01) == 0; 139 | } 140 | } 141 | 142 | function DataMask101() 143 | { 144 | this.unmaskBitMatrix=function(bits, dimension) 145 | { 146 | for (var i = 0; i < dimension; i++) 147 | { 148 | for (var j = 0; j < dimension; j++) 149 | { 150 | if (this.isMasked(i, j)) 151 | { 152 | bits.flip(j, i); 153 | } 154 | } 155 | } 156 | } 157 | this.isMasked=function( i, j) 158 | { 159 | var temp = i * j; 160 | return (temp & 0x01) + (temp % 3) == 0; 161 | } 162 | } 163 | 164 | function DataMask110() 165 | { 166 | this.unmaskBitMatrix=function(bits, dimension) 167 | { 168 | for (var i = 0; i < dimension; i++) 169 | { 170 | for (var j = 0; j < dimension; j++) 171 | { 172 | if (this.isMasked(i, j)) 173 | { 174 | bits.flip(j, i); 175 | } 176 | } 177 | } 178 | } 179 | this.isMasked=function( i, j) 180 | { 181 | var temp = i * j; 182 | return (((temp & 0x01) + (temp % 3)) & 0x01) == 0; 183 | } 184 | } 185 | function DataMask111() 186 | { 187 | this.unmaskBitMatrix=function(bits, dimension) 188 | { 189 | for (var i = 0; i < dimension; i++) 190 | { 191 | for (var j = 0; j < dimension; j++) 192 | { 193 | if (this.isMasked(i, j)) 194 | { 195 | bits.flip(j, i); 196 | } 197 | } 198 | } 199 | } 200 | this.isMasked=function( i, j) 201 | { 202 | return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0; 203 | } 204 | } 205 | 206 | DataMask.DATA_MASKS = new Array(new DataMask000(), new DataMask001(), new DataMask010(), new DataMask011(), new DataMask100(), new DataMask101(), new DataMask110(), new DataMask111()); 207 | 208 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/decoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | Decoder={}; 27 | Decoder.rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD); 28 | 29 | Decoder.correctErrors=function( codewordBytes, numDataCodewords) 30 | { 31 | var numCodewords = codewordBytes.length; 32 | // First read into an array of ints 33 | var codewordsInts = new Array(numCodewords); 34 | for (var i = 0; i < numCodewords; i++) 35 | { 36 | codewordsInts[i] = codewordBytes[i] & 0xFF; 37 | } 38 | var numECCodewords = codewordBytes.length - numDataCodewords; 39 | try 40 | { 41 | Decoder.rsDecoder.decode(codewordsInts, numECCodewords); 42 | //var corrector = new ReedSolomon(codewordsInts, numECCodewords); 43 | //corrector.correct(); 44 | } 45 | catch ( rse) 46 | { 47 | throw rse; 48 | } 49 | // Copy back into array of bytes -- only need to worry about the bytes that were data 50 | // We don't care about errors in the error-correction codewords 51 | for (var i = 0; i < numDataCodewords; i++) 52 | { 53 | codewordBytes[i] = codewordsInts[i]; 54 | } 55 | } 56 | 57 | Decoder.decode=function(bits) 58 | { 59 | var parser = new BitMatrixParser(bits); 60 | var version = parser.readVersion(); 61 | var ecLevel = parser.readFormatInformation().ErrorCorrectionLevel; 62 | 63 | // Read codewords 64 | var codewords = parser.readCodewords(); 65 | 66 | // Separate into data blocks 67 | var dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel); 68 | 69 | // Count total number of data bytes 70 | var totalBytes = 0; 71 | for (var i = 0; i < dataBlocks.length; i++) 72 | { 73 | totalBytes += dataBlocks[i].NumDataCodewords; 74 | } 75 | var resultBytes = new Array(totalBytes); 76 | var resultOffset = 0; 77 | 78 | // Error-correct and copy data blocks together into a stream of bytes 79 | for (var j = 0; j < dataBlocks.length; j++) 80 | { 81 | var dataBlock = dataBlocks[j]; 82 | var codewordBytes = dataBlock.Codewords; 83 | var numDataCodewords = dataBlock.NumDataCodewords; 84 | Decoder.correctErrors(codewordBytes, numDataCodewords); 85 | for (var i = 0; i < numDataCodewords; i++) 86 | { 87 | resultBytes[resultOffset++] = codewordBytes[i]; 88 | } 89 | } 90 | 91 | // Decode the contents of that stream of bytes 92 | var reader = new QRCodeDataBlockReader(resultBytes, version.VersionNumber, ecLevel.Bits); 93 | return reader; 94 | //return DecodedBitStreamParser.decode(resultBytes, version, ecLevel); 95 | } 96 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/errorlevel.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function ErrorCorrectionLevel(ordinal, bits, name) 27 | { 28 | this.ordinal_Renamed_Field = ordinal; 29 | this.bits = bits; 30 | this.name = name; 31 | this.__defineGetter__("Bits", function() 32 | { 33 | return this.bits; 34 | }); 35 | this.__defineGetter__("Name", function() 36 | { 37 | return this.name; 38 | }); 39 | this.ordinal=function() 40 | { 41 | return this.ordinal_Renamed_Field; 42 | } 43 | } 44 | 45 | ErrorCorrectionLevel.forBits=function( bits) 46 | { 47 | if (bits < 0 || bits >= FOR_BITS.length) 48 | { 49 | throw "ArgumentException"; 50 | } 51 | return FOR_BITS[bits]; 52 | } 53 | 54 | var L = new ErrorCorrectionLevel(0, 0x01, "L"); 55 | var M = new ErrorCorrectionLevel(1, 0x00, "M"); 56 | var Q = new ErrorCorrectionLevel(2, 0x03, "Q"); 57 | var H = new ErrorCorrectionLevel(3, 0x02, "H"); 58 | var FOR_BITS = new Array( M, L, H, Q); 59 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/formatinf.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | var FORMAT_INFO_MASK_QR = 0x5412; 27 | var FORMAT_INFO_DECODE_LOOKUP = new Array(new Array(0x5412, 0x00), new Array(0x5125, 0x01), new Array(0x5E7C, 0x02), new Array(0x5B4B, 0x03), new Array(0x45F9, 0x04), new Array(0x40CE, 0x05), new Array(0x4F97, 0x06), new Array(0x4AA0, 0x07), new Array(0x77C4, 0x08), new Array(0x72F3, 0x09), new Array(0x7DAA, 0x0A), new Array(0x789D, 0x0B), new Array(0x662F, 0x0C), new Array(0x6318, 0x0D), new Array(0x6C41, 0x0E), new Array(0x6976, 0x0F), new Array(0x1689, 0x10), new Array(0x13BE, 0x11), new Array(0x1CE7, 0x12), new Array(0x19D0, 0x13), new Array(0x0762, 0x14), new Array(0x0255, 0x15), new Array(0x0D0C, 0x16), new Array(0x083B, 0x17), new Array(0x355F, 0x18), new Array(0x3068, 0x19), new Array(0x3F31, 0x1A), new Array(0x3A06, 0x1B), new Array(0x24B4, 0x1C), new Array(0x2183, 0x1D), new Array(0x2EDA, 0x1E), new Array(0x2BED, 0x1F)); 28 | var BITS_SET_IN_HALF_BYTE = new Array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); 29 | 30 | 31 | function FormatInformation(formatInfo) 32 | { 33 | this.errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03); 34 | this.dataMask = (formatInfo & 0x07); 35 | 36 | this.__defineGetter__("ErrorCorrectionLevel", function() 37 | { 38 | return this.errorCorrectionLevel; 39 | }); 40 | this.__defineGetter__("DataMask", function() 41 | { 42 | return this.dataMask; 43 | }); 44 | this.GetHashCode=function() 45 | { 46 | return (this.errorCorrectionLevel.ordinal() << 3) | dataMask; 47 | } 48 | this.Equals=function( o) 49 | { 50 | var other = o; 51 | return this.errorCorrectionLevel == other.errorCorrectionLevel && this.dataMask == other.dataMask; 52 | } 53 | } 54 | 55 | FormatInformation.numBitsDiffering=function( a, b) 56 | { 57 | a ^= b; // a now has a 1 bit exactly where its bit differs with b's 58 | // Count bits set quickly with a series of lookups: 59 | return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(URShift(a, 4) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 8) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 12) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 16) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 20) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 24) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 28) & 0x0F)]; 60 | } 61 | 62 | FormatInformation.decodeFormatInformation=function( maskedFormatInfo) 63 | { 64 | var formatInfo = FormatInformation.doDecodeFormatInformation(maskedFormatInfo); 65 | if (formatInfo != null) 66 | { 67 | return formatInfo; 68 | } 69 | // Should return null, but, some QR codes apparently 70 | // do not mask this info. Try again by actually masking the pattern 71 | // first 72 | return FormatInformation.doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR); 73 | } 74 | FormatInformation.doDecodeFormatInformation=function( maskedFormatInfo) 75 | { 76 | // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing 77 | var bestDifference = 0xffffffff; 78 | var bestFormatInfo = 0; 79 | for (var i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) 80 | { 81 | var decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i]; 82 | var targetInfo = decodeInfo[0]; 83 | if (targetInfo == maskedFormatInfo) 84 | { 85 | // Found an exact match 86 | return new FormatInformation(decodeInfo[1]); 87 | } 88 | var bitsDifference = this.numBitsDiffering(maskedFormatInfo, targetInfo); 89 | if (bitsDifference < bestDifference) 90 | { 91 | bestFormatInfo = decodeInfo[1]; 92 | bestDifference = bitsDifference; 93 | } 94 | } 95 | // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits 96 | // differing means we found a match 97 | if (bestDifference <= 3) 98 | { 99 | return new FormatInformation(bestFormatInfo); 100 | } 101 | return null; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/gf256.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function GF256( primitive) 27 | { 28 | this.expTable = new Array(256); 29 | this.logTable = new Array(256); 30 | var x = 1; 31 | for (var i = 0; i < 256; i++) 32 | { 33 | this.expTable[i] = x; 34 | x <<= 1; // x = x * 2; we're assuming the generator alpha is 2 35 | if (x >= 0x100) 36 | { 37 | x ^= primitive; 38 | } 39 | } 40 | for (var i = 0; i < 255; i++) 41 | { 42 | this.logTable[this.expTable[i]] = i; 43 | } 44 | // logTable[0] == 0 but this should never be used 45 | var at0=new Array(1);at0[0]=0; 46 | this.zero = new GF256Poly(this, new Array(at0)); 47 | var at1=new Array(1);at1[0]=1; 48 | this.one = new GF256Poly(this, new Array(at1)); 49 | 50 | this.__defineGetter__("Zero", function() 51 | { 52 | return this.zero; 53 | }); 54 | this.__defineGetter__("One", function() 55 | { 56 | return this.one; 57 | }); 58 | this.buildMonomial=function( degree, coefficient) 59 | { 60 | if (degree < 0) 61 | { 62 | throw "System.ArgumentException"; 63 | } 64 | if (coefficient == 0) 65 | { 66 | return zero; 67 | } 68 | var coefficients = new Array(degree + 1); 69 | for(var i=0;i 1 && coefficients[0] == 0) 35 | { 36 | // Leading term must be non-zero for anything except the constant polynomial "0" 37 | var firstNonZero = 1; 38 | while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) 39 | { 40 | firstNonZero++; 41 | } 42 | if (firstNonZero == coefficientsLength) 43 | { 44 | this.coefficients = field.Zero.coefficients; 45 | } 46 | else 47 | { 48 | this.coefficients = new Array(coefficientsLength - firstNonZero); 49 | for(var i=0;i largerCoefficients.length) 121 | { 122 | var temp = smallerCoefficients; 123 | smallerCoefficients = largerCoefficients; 124 | largerCoefficients = temp; 125 | } 126 | var sumDiff = new Array(largerCoefficients.length); 127 | var lengthDiff = largerCoefficients.length - smallerCoefficients.length; 128 | // Copy high-order terms only found in higher-degree polynomial's coefficients 129 | //Array.Copy(largerCoefficients, 0, sumDiff, 0, lengthDiff); 130 | for(var ci=0;ci= other.Degree && !remainder.Zero) 219 | { 220 | var degreeDifference = remainder.Degree - other.Degree; 221 | var scale = this.field.multiply(remainder.getCoefficient(remainder.Degree), inverseDenominatorLeadingTerm); 222 | var term = other.multiplyByMonomial(degreeDifference, scale); 223 | var iterationQuotient = this.field.buildMonomial(degreeDifference, scale); 224 | quotient = quotient.addOrSubtract(iterationQuotient); 225 | remainder = remainder.addOrSubtract(term); 226 | } 227 | 228 | return new Array(quotient, remainder); 229 | } 230 | } -------------------------------------------------------------------------------- /public/src/js/jsqrcode/grid.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | GridSampler = {}; 27 | 28 | GridSampler.checkAndNudgePoints=function( image, points) 29 | { 30 | var width = qrcode.width; 31 | var height = qrcode.height; 32 | // Check and nudge points from start until we see some that are OK: 33 | var nudged = true; 34 | for (var offset = 0; offset < points.length && nudged; offset += 2) 35 | { 36 | var x = Math.floor (points[offset]); 37 | var y = Math.floor( points[offset + 1]); 38 | if (x < - 1 || x > width || y < - 1 || y > height) 39 | { 40 | throw "Error.checkAndNudgePoints "; 41 | } 42 | nudged = false; 43 | if (x == - 1) 44 | { 45 | points[offset] = 0.0; 46 | nudged = true; 47 | } 48 | else if (x == width) 49 | { 50 | points[offset] = width - 1; 51 | nudged = true; 52 | } 53 | if (y == - 1) 54 | { 55 | points[offset + 1] = 0.0; 56 | nudged = true; 57 | } 58 | else if (y == height) 59 | { 60 | points[offset + 1] = height - 1; 61 | nudged = true; 62 | } 63 | } 64 | // Check and nudge points from end: 65 | nudged = true; 66 | for (var offset = points.length - 2; offset >= 0 && nudged; offset -= 2) 67 | { 68 | var x = Math.floor( points[offset]); 69 | var y = Math.floor( points[offset + 1]); 70 | if (x < - 1 || x > width || y < - 1 || y > height) 71 | { 72 | throw "Error.checkAndNudgePoints "; 73 | } 74 | nudged = false; 75 | if (x == - 1) 76 | { 77 | points[offset] = 0.0; 78 | nudged = true; 79 | } 80 | else if (x == width) 81 | { 82 | points[offset] = width - 1; 83 | nudged = true; 84 | } 85 | if (y == - 1) 86 | { 87 | points[offset + 1] = 0.0; 88 | nudged = true; 89 | } 90 | else if (y == height) 91 | { 92 | points[offset + 1] = height - 1; 93 | nudged = true; 94 | } 95 | } 96 | } 97 | 98 | 99 | 100 | GridSampler.sampleGrid3=function( image, dimension, transform) 101 | { 102 | var bits = new BitMatrix(dimension); 103 | var points = new Array(dimension << 1); 104 | for (var y = 0; y < dimension; y++) 105 | { 106 | var max = points.length; 107 | var iValue = y + 0.5; 108 | for (var x = 0; x < max; x += 2) 109 | { 110 | points[x] = (x >> 1) + 0.5; 111 | points[x + 1] = iValue; 112 | } 113 | transform.transformPoints1(points); 114 | // Quick check to see if points transformed to something inside the image; 115 | // sufficient to check the endpoints 116 | GridSampler.checkAndNudgePoints(image, points); 117 | try 118 | { 119 | for (var x = 0; x < max; x += 2) 120 | { 121 | var xpoint = (Math.floor( points[x]) * 4) + (Math.floor( points[x + 1]) * qrcode.width * 4); 122 | var bit = image[Math.floor( points[x])+ qrcode.width* Math.floor( points[x + 1])]; 123 | qrcode.imagedata.data[xpoint] = bit?255:0; 124 | qrcode.imagedata.data[xpoint+1] = bit?255:0; 125 | qrcode.imagedata.data[xpoint+2] = 0; 126 | qrcode.imagedata.data[xpoint+3] = 255; 127 | //bits[x >> 1][ y]=bit; 128 | if(bit) 129 | bits.set_Renamed(x >> 1, y); 130 | } 131 | } 132 | catch ( aioobe) 133 | { 134 | // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting 135 | // transform gets "twisted" such that it maps a straight line of points to a set of points 136 | // whose endpoints are in bounds, but others are not. There is probably some mathematical 137 | // way to detect this about the transformation that I don't know yet. 138 | // This results in an ugly runtime exception despite our clever checks above -- can't have 139 | // that. We could check each point's coordinates but that feels duplicative. We settle for 140 | // catching and wrapping ArrayIndexOutOfBoundsException. 141 | throw "Error.checkAndNudgePoints"; 142 | } 143 | } 144 | return bits; 145 | } 146 | 147 | GridSampler.sampleGridx=function( image, dimension, p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY) 148 | { 149 | var transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); 150 | 151 | return GridSampler.sampleGrid3(image, dimension, transform); 152 | } 153 | -------------------------------------------------------------------------------- /public/src/js/jsqrcode/rsdecoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function ReedSolomonDecoder(field) 27 | { 28 | this.field = field; 29 | this.decode=function(received, twoS) 30 | { 31 | var poly = new GF256Poly(this.field, received); 32 | var syndromeCoefficients = new Array(twoS); 33 | for(var i=0;i= b's 70 | if (a.Degree < b.Degree) 71 | { 72 | var temp = a; 73 | a = b; 74 | b = temp; 75 | } 76 | 77 | var rLast = a; 78 | var r = b; 79 | var sLast = this.field.One; 80 | var s = this.field.Zero; 81 | var tLast = this.field.Zero; 82 | var t = this.field.One; 83 | 84 | // Run Euclidean algorithm until r's degree is less than R/2 85 | while (r.Degree >= Math.floor(R / 2)) 86 | { 87 | var rLastLast = rLast; 88 | var sLastLast = sLast; 89 | var tLastLast = tLast; 90 | rLast = r; 91 | sLast = s; 92 | tLast = t; 93 | 94 | // Divide rLastLast by rLast, with quotient in q and remainder in r 95 | if (rLast.Zero) 96 | { 97 | // Oops, Euclidean algorithm already terminated? 98 | throw "r_{i-1} was zero"; 99 | } 100 | r = rLastLast; 101 | var q = this.field.Zero; 102 | var denominatorLeadingTerm = rLast.getCoefficient(rLast.Degree); 103 | var dltInverse = this.field.inverse(denominatorLeadingTerm); 104 | while (r.Degree >= rLast.Degree && !r.Zero) 105 | { 106 | var degreeDiff = r.Degree - rLast.Degree; 107 | var scale = this.field.multiply(r.getCoefficient(r.Degree), dltInverse); 108 | q = q.addOrSubtract(this.field.buildMonomial(degreeDiff, scale)); 109 | r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); 110 | //r.EXE(); 111 | } 112 | 113 | s = q.multiply1(sLast).addOrSubtract(sLastLast); 114 | t = q.multiply1(tLast).addOrSubtract(tLastLast); 115 | } 116 | 117 | var sigmaTildeAtZero = t.getCoefficient(0); 118 | if (sigmaTildeAtZero == 0) 119 | { 120 | throw "ReedSolomonException sigmaTilde(0) was zero"; 121 | } 122 | 123 | var inverse = this.field.inverse(sigmaTildeAtZero); 124 | var sigma = t.multiply2(inverse); 125 | var omega = r.multiply2(inverse); 126 | return new Array(sigma, omega); 127 | } 128 | this.findErrorLocations=function( errorLocator) 129 | { 130 | // This is a direct application of Chien's search 131 | var numErrors = errorLocator.Degree; 132 | if (numErrors == 1) 133 | { 134 | // shortcut 135 | return new Array(errorLocator.getCoefficient(1)); 136 | } 137 | var result = new Array(numErrors); 138 | var e = 0; 139 | for (var i = 1; i < 256 && e < numErrors; i++) 140 | { 141 | if (errorLocator.evaluateAt(i) == 0) 142 | { 143 | result[e] = this.field.inverse(i); 144 | e++; 145 | } 146 | } 147 | if (e != numErrors) 148 | { 149 | throw "Error locator degree does not match number of roots"; 150 | } 151 | return result; 152 | } 153 | this.findErrorMagnitudes=function( errorEvaluator, errorLocations, dataMatrix) 154 | { 155 | // This is directly applying Forney's Formula 156 | var s = errorLocations.length; 157 | var result = new Array(s); 158 | for (var i = 0; i < s; i++) 159 | { 160 | var xiInverse = this.field.inverse(errorLocations[i]); 161 | var denominator = 1; 162 | for (var j = 0; j < s; j++) 163 | { 164 | if (i != j) 165 | { 166 | denominator = this.field.multiply(denominator, GF256.addOrSubtract(1, this.field.multiply(errorLocations[j], xiInverse))); 167 | } 168 | } 169 | result[i] = this.field.multiply(errorEvaluator.evaluateAt(xiInverse), this.field.inverse(denominator)); 170 | // Thanks to sanfordsquires for this fix: 171 | if (dataMatrix) 172 | { 173 | result[i] = this.field.multiply(result[i], xiInverse); 174 | } 175 | } 176 | return result; 177 | } 178 | } -------------------------------------------------------------------------------- /public/src/js/jsqrcode/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | QRCODE 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 131 | 132 | 133 | 134 | 135 |
136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
145 |
146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /public/src/js/services/address.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.address') 4 | .factory('Address', 5 | function ($resource) { 6 | return $resource(window.apiPrefix + '/addr/:addrStr/?noTxList=1', { 7 | addrStr: '@addStr' 8 | }, { 9 | get: { 10 | method: 'GET', 11 | interceptor: { 12 | response: function (res) { 13 | return res.data; 14 | }, 15 | responseError: function (res) { 16 | if (res.status === 404) { 17 | return res; 18 | } 19 | } 20 | } 21 | } 22 | }); 23 | }) 24 | .factory('AddressValidator', 25 | function () { 26 | return { 27 | test: function (addressStr) { 28 | 29 | return /^[Xy789][1-9A-Za-z][^OIl]{20,40}/.test(addressStr); 30 | } 31 | }; 32 | }); 33 | -------------------------------------------------------------------------------- /public/src/js/services/blocks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.blocks') 4 | .factory('Block', 5 | function ($resource) { 6 | return $resource(window.apiPrefix + '/block/:blockHash', { 7 | blockHash: '@blockHash' 8 | }, { 9 | get: { 10 | method: 'GET', 11 | interceptor: { 12 | response: function (res) { 13 | return res.data; 14 | }, 15 | responseError: function (res) { 16 | if (res.status === 404) { 17 | return res; 18 | } 19 | } 20 | } 21 | } 22 | }); 23 | }) 24 | .factory('Blocks', 25 | function ($resource) { 26 | return $resource(window.apiPrefix + '/blocks'); 27 | }) 28 | .factory('BlockHashValidator', 29 | function (HashValidator) { 30 | return { 31 | test: function (blockHashStr, network) { 32 | return HashValidator.test64(blockHashStr) || 33 | (HashValidator.test66(blockHashStr) && blockHashStr.startsWith('0x')); 34 | } 35 | }; 36 | }) 37 | .factory('BlockByHeight', 38 | function ($resource) { 39 | return $resource(window.apiPrefix + '/block-index/:blockHeight'); 40 | }); 41 | -------------------------------------------------------------------------------- /public/src/js/services/currency.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.currency').factory('Currency', 4 | function($resource) { 5 | return $resource(window.apiPrefix + '/currency'); 6 | }); 7 | -------------------------------------------------------------------------------- /public/src/js/services/global.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | //Global service for global variables 4 | angular.module('insight.system') 5 | .factory('Global',[ 6 | function() { 7 | return { 8 | get: function () { 9 | return null; 10 | } 11 | } 12 | } 13 | ]) 14 | .factory('Version', 15 | function($resource) { 16 | return $resource(window.apiPrefix + '/version'); 17 | }); 18 | -------------------------------------------------------------------------------- /public/src/js/services/hash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.hash') 4 | .factory('HashValidator', 5 | function () { 6 | return { 7 | test64: function (hashStr) { 8 | return typeof hashStr === 'string' && /^(0x)?[0-9a-f]{64}$/i.test(hashStr); 9 | }, 10 | test66: function (hashStr) { 11 | return typeof hashStr === 'string' && /^(0x)?[0-9a-f]{66}$/i.test(hashStr); 12 | } 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /public/src/js/services/socket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var ScopedSocket = function(socket, $rootScope) { 4 | this.socket = socket; 5 | this.$rootScope = $rootScope; 6 | this.listeners = []; 7 | }; 8 | 9 | ScopedSocket.prototype.removeAllListeners = function(opts) { 10 | if (!opts) opts = {}; 11 | for (var i = 0; i < this.listeners.length; i++) { 12 | var details = this.listeners[i]; 13 | if (opts.skipConnect && details.event === 'connect') { 14 | continue; 15 | } 16 | this.socket.removeListener(details.event, details.fn); 17 | } 18 | this.listeners = []; 19 | }; 20 | 21 | ScopedSocket.prototype.on = function(event, callback) { 22 | var socket = this.socket; 23 | var $rootScope = this.$rootScope; 24 | 25 | var wrapped_callback = function() { 26 | var args = arguments; 27 | $rootScope.$apply(function() { 28 | callback.apply(socket, args); 29 | }); 30 | }; 31 | socket.on(event, wrapped_callback); 32 | 33 | this.listeners.push({ 34 | event: event, 35 | fn: wrapped_callback 36 | }); 37 | }; 38 | 39 | ScopedSocket.prototype.emit = function(event, data, callback) { 40 | var socket = this.socket; 41 | var $rootScope = this.$rootScope; 42 | var args = Array.prototype.slice.call(arguments); 43 | 44 | args.push(function() { 45 | var args = arguments; 46 | $rootScope.$apply(function() { 47 | if (callback) { 48 | callback.apply(socket, args); 49 | } 50 | }); 51 | }); 52 | 53 | socket.emit.apply(socket, args); 54 | }; 55 | 56 | angular.module('insight.socket').factory('getSocket', 57 | function($rootScope) { 58 | var socket = io.connect(null, { 59 | 'reconnect': true, 60 | 'reconnection delay': 500, 61 | }); 62 | return function(scope) { 63 | var scopedSocket = new ScopedSocket(socket, $rootScope); 64 | scope.$on('$destroy', function() { 65 | scopedSocket.removeAllListeners(); 66 | }); 67 | socket.on('connect', function() { 68 | scopedSocket.removeAllListeners({ 69 | skipConnect: true 70 | }); 71 | }); 72 | return scopedSocket; 73 | }; 74 | }); 75 | -------------------------------------------------------------------------------- /public/src/js/services/status.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.status') 4 | .factory('Status', 5 | function($resource) { 6 | return $resource(window.apiPrefix + '/status', { 7 | q: '@q' 8 | }); 9 | }) 10 | .factory('Sync', 11 | function($resource) { 12 | return $resource(window.apiPrefix + '/sync'); 13 | }) 14 | .factory('PeerSync', 15 | function($resource) { 16 | return $resource(window.apiPrefix + '/peer'); 17 | }); 18 | -------------------------------------------------------------------------------- /public/src/js/services/transactions.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('insight.transactions') 4 | .factory('Transaction', 5 | function ($resource) { 6 | return $resource(window.apiPrefix + '/tx/:txId', { 7 | txId: '@txId' 8 | }, { 9 | get: { 10 | method: 'GET', 11 | interceptor: { 12 | response: function (res) { 13 | return res.data; 14 | }, 15 | responseError: function (res) { 16 | if (res.status === 404) { 17 | return res; 18 | } 19 | } 20 | } 21 | } 22 | }); 23 | }) 24 | .factory('TransactionsByBlock', 25 | function ($resource) { 26 | return $resource(window.apiPrefix + '/txs', { 27 | block: '@block' 28 | }); 29 | }) 30 | .factory('TransactionsByAddress', 31 | function ($resource) { 32 | return $resource(window.apiPrefix + '/txs', { 33 | address: '@address' 34 | }); 35 | }) 36 | 37 | .factory('Transactions', 38 | function ($resource) { 39 | return $resource(window.apiPrefix + '/txs'); 40 | }) 41 | .factory('TransactionHashValidator', 42 | function (HashValidator, BlockHashValidator) { 43 | return { 44 | test: function (txHashStr) { 45 | return HashValidator.test64(txHashStr) && !BlockHashValidator.test(txHashStr); 46 | } 47 | }; 48 | }); 49 | -------------------------------------------------------------------------------- /public/views/404.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Ooops!

4 |

404 Page not found :(

5 |

Go to home

6 |
7 | -------------------------------------------------------------------------------- /public/views/address.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 20 |

Address {{$root.currency.getConvertion(address.balance) || address.balance + ' DASH'}}

21 |
22 | Loading Address Information 23 |
24 |
25 |
26 | Address 27 | {{address.addrStr}} 28 | 29 |
30 |

Summary confirmed

31 |
32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
Total Received{{$root.currency.getConvertion(address.totalReceived) || address.totalReceived + ' DASH'}}
Total Sent{{$root.currency.getConvertion(address.totalSent) || address.totalSent + ' DASH'}}
Final Balance{{$root.currency.getConvertion(address.balance) || address.balance + ' DASH'}}
No. Transactions{{address.txApperances}}
53 |
54 |
55 | 56 |
57 |
58 |
59 |

Unconfirmed

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
Unconfirmed Txs Balance{{$root.currency.getConvertion(address.unconfirmedBalance)}}
No. Transactions{{address.unconfirmedTxApperances}}
73 |
74 |
75 |
76 |

Transactions

77 |
78 |
79 |
80 | 81 | -------------------------------------------------------------------------------- /public/views/block.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 34 |

Block #{{block.height}}

35 |
36 | Loading Block Information 37 |
38 |
39 |
40 | BlockHash 41 | {{block.hash}} 42 | 43 |
44 |

Summary

45 |
46 |
47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 73 | 74 | 75 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 88 |
Number Of Transactions{{block.tx.length}}
Height{{block.height}} 56 | (Mainchain) 57 | (Orphaned) 58 |
Block Reward{{$root.currency.getConvertion(block.reward) || block.reward + ' DASH'}}
Timestamp{{block.time * 1000 | date:'medium'}}
Mined by 71 | {{block.poolInfo.poolName}} 72 |
Merkle Root 77 |
78 | 79 | {{block.merkleroot}} 80 |
81 |
Previous Block{{block.height-1}}
89 |
90 |
91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 |
Difficulty{{block.difficulty}}
Bits{{block.bits}}
Size (bytes){{block.size}}
Version{{block.version}}
Nonce{{block.nonce}}
Next Block{{block.height+1}}
119 |
120 |
121 |
122 |
123 |

Transactions

124 |
125 |
126 | 127 |
128 | 129 | -------------------------------------------------------------------------------- /public/views/block_list.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | 8 |

Blocks
9 | mined on:

10 |
11 |
12 |

13 | {{pagination.current}} UTC 14 |

15 |

16 |
17 | Loading Selected Date... 18 |
19 |
20 |

 

21 |

Today

22 |

{{humanSince(pagination.currentTs)}} 23 |

 

24 | 28 |
29 |
30 |
31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
HeightTimestampTransactionsSize
Waiting for blocks...
{{b.height}}{{millisToUTCDate(b.time) | date:'medium'}} UTC{{b.txlength}}{{b.size}}
60 | 64 |
65 |
66 |

No blocks yet.

68 |
69 | 70 | -------------------------------------------------------------------------------- /public/views/dummy-translations.html: -------------------------------------------------------------------------------- 1 | {{'Blocks'|translate}} 2 | {{'Status'|translate}} 3 | -------------------------------------------------------------------------------- /public/views/includes/connection.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
6 | 7 | Error! 8 | 9 |

10 | Can't connect to dashd to get live updates from the p2p network. (Tried connecting to dashd at {{host}}:{{port}} and failed.) 11 |

12 | 13 |

14 | Can't connect to insight server. Attempting to reconnect... 15 |

16 | 17 |

18 | Can't connect to internet. Please, check your connection. 19 |

20 | 21 |
22 |
23 | -------------------------------------------------------------------------------- /public/views/includes/currency.html: -------------------------------------------------------------------------------- 1 | 2 | {{currency.symbol}} 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /public/views/includes/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 16 | 51 |
52 |
53 | -------------------------------------------------------------------------------- /public/views/includes/infoStatus.html: -------------------------------------------------------------------------------- 1 | Loading... 2 | {{error}} 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/views/includes/search.html: -------------------------------------------------------------------------------- 1 | 7 | -------------------------------------------------------------------------------- /public/views/index.html: -------------------------------------------------------------------------------- 1 |
2 | {{$root.flashMessage}} 3 |
4 |
5 |
6 |
7 |
8 |
9 | 10 |
11 | 12 |

Latest Blocks

13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 |
HeightAgeTransactionsSize
Waiting for blocks...
27 | {{b.height}} 28 | {{humanSince(b.time)}}{{b.txlength}}{{b.size}}
36 |
37 | See all blocks 38 |
39 | 40 |

Latest Transactions

41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 55 | 56 | 57 | 58 |
HashValue Out
Waiting for transactions...
53 | {{tx.txid}} 54 | {{$root.currency.getConvertion(tx.valueOut) || tx.valueOut + ' DASH'}}
59 | 60 |
61 | 62 |
63 |

About

64 |

insight is an open-source Dash blockchain explorer with complete REST and websocket APIs that can be used for writing web wallets and other apps that need more advanced blockchain queries than provided by dashd RPC. Check out the source code.

66 |

insight is still in development, so be sure to report any bugs and provide feedback for improvement at our github issue tracker.

67 |
68 |
69 | Powered by 70 |
71 | 72 | 73 | 74 | 75 |
76 |
77 |
78 |
79 |
80 | -------------------------------------------------------------------------------- /public/views/messages_verify.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 |
9 |
10 |
11 | 14 |
15 | 17 |
18 |
19 |
20 | 23 |
24 | 26 |
27 |
28 |
29 | 32 |
33 | 35 |
36 |
37 |
38 |
39 | 43 |
44 |
45 |
46 |
47 |
49 |
50 | Loading... 51 |
52 |
54 | The message is verifiably from {{verification.address}}. 55 |
56 |
58 | The message failed to verify. 59 |
60 |
62 |

An error occured in the verification process.

63 |

64 | Error message: 65 | {{verification.error}} 66 |

67 |
68 |
69 |
70 |
71 |
72 |

73 | Dash comes with a way of signing arbitrary messages. 74 |

75 |

76 | This form can be used to verify that a message comes from 77 | a specific Dash address. 78 |

79 |
80 |
81 |
82 | -------------------------------------------------------------------------------- /public/views/redirect.html: -------------------------------------------------------------------------------- 1 |
Redirecting...
2 | -------------------------------------------------------------------------------- /public/views/status.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 6 |
7 | 8 |
9 |

Sync Status

10 | 11 | 12 | 13 | 14 | 23 | 24 | 25 | 26 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 |
Sync Progress 15 |
16 | 17 | {{sync.syncPercentage}}% 18 | Complete 19 | 20 | 21 |
22 |
Current Sync Status 27 | {{sync.status}} 28 | 29 | 30 | {{sync.error}} 31 | 32 |
Start Date
Finish Date
Initial Block Chain Height{{sync.blockChainHeight}}
Synced Blocks{{sync.syncedBlocks}}
Skipped Blocks (previously synced){{sync.skippedBlocks}}
Sync Type{{sync.type}}
60 | 61 |

Last Block

62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
Last Block Hash (dashd){{lastblockhash}}
Current Blockchain Tip (insight){{syncTipHash}}
75 | 76 |

Best ChainLock

77 | 78 | 79 | 80 | 81 | 82 | 86 | 87 | 88 | 89 | 93 | 94 | 95 |
ChainLock block hash 83 | {{bestchainlock.blockhash}} 84 | {{bestchainlock.blockhash}} 85 |
ChainLock block height 90 | {{bestchainlock.height}} 91 | {{bestchainlock.blockhash}} 92 |
96 | 97 |
98 | 99 |
100 |

Dash node information

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 |
Version{{info.version}}
Protocol version{{info.protocolversion}}
Blocks{{info.blocks}}
Time Offset{{info.timeoffset}}
Connections to other nodes{{info.connections}}
Mining Difficulty{{info.difficulty}}
Network{{info.network}}
Proxy setting{{info.proxy}}
Info Errors{{info.infoErrors}}
142 |
143 |
144 |
145 | -------------------------------------------------------------------------------- /public/views/transaction.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 29 |
30 |

Transaction 31 | 32 | Input 33 | Output 34 | {{v_index}} 35 | 36 |

37 |
38 |
39 | Loading Transaction Details 40 |
41 |
42 |
43 |
44 | Transaction 45 | {{tx.txid}} 46 | 47 |
48 |

Summary

49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 89 | 90 | 91 |
Size{{tx.size}} (bytes)
Fee Rate{{$root.currency.getConvertion((tx.fees * 1000) / tx.size) + ' per kB' || ((tx.fees * 1000) / tx.size) + 'DASH per kB'}}
Received Time{{tx.time * 1000|date:'medium'}}N/A
Mined Time{{tx.blocktime * 1000|date:'medium'}}N/A
Included in Block 72 |
73 | {{tx.blockhash}} 74 |
75 |
Unconfirmed
LockTime{{tx.locktime}}
Coinbase 84 |
85 | 86 | {{tx.vin[0].coinbase}} 87 |
88 |
92 |
93 |

Details

94 |
95 |
96 |
97 |
98 |
99 | -------------------------------------------------------------------------------- /public/views/transaction/list.html: -------------------------------------------------------------------------------- 1 |
There are no transactions involving this address.
4 |
5 |
6 |
7 |
8 |
9 | Loading Transactions... 10 |
11 |
12 | -------------------------------------------------------------------------------- /public/views/transaction_sendraw.html: -------------------------------------------------------------------------------- 1 |
2 | 7 |
8 |
9 |
10 |
12 | 15 |
16 | 19 | 20 | Raw transaction data must be a valid hexadecimal string. 21 | 22 |
23 |
24 |
25 |
26 | 30 |
31 |
32 |
33 |
34 |
36 |
37 | Loading... 38 |
39 |
40 | Transaction succesfully broadcast.
Transaction id: {{txid}} 41 |
42 |
43 | An error occured:
{{error}} 44 |
45 |
46 |
47 |
48 |
49 |

50 | This form can be used to broadcast a raw transaction in hex format over 51 | the Dash network. 52 |

53 |
54 |
55 |
56 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "jasmine": true, 5 | "protractor": true 6 | }, 7 | "rules": { 8 | "import/no-extraneous-dependencies": "off" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /test/e2e/conf.js: -------------------------------------------------------------------------------- 1 | exports.config = { 2 | framework: 'jasmine', 3 | seleniumAddress: 'http://localhost:4444/wd/hub', 4 | jasmineNodeOpts: { 5 | defaultTimeoutInterval: 250000, 6 | }, 7 | specs: ['test.js'], 8 | multiCapabilities: [ 9 | { 10 | browserName: 'firefox', 11 | 'moz:firefoxOptions': { 12 | args: ['--headless'], 13 | }, 14 | }, 15 | { 16 | chromeOptions: { 17 | args: ['--headless'], 18 | }, 19 | browserName: 'chrome', 20 | }, 21 | ], 22 | }; 23 | -------------------------------------------------------------------------------- /test/e2e/test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { 3 | Builder, Browser, 4 | } = require('selenium-webdriver'); 5 | const chrome = require('selenium-webdriver/chrome'); 6 | const wait = require('../../lib/test/util/wait'); 7 | 8 | const TopPanel = require('../../lib/test/pages/TopPanel'); 9 | const StatusPage = require('../../lib/test/pages/StatusPage'); 10 | const BlockPage = require('../../lib/test/pages/BlockPage'); 11 | 12 | describe('basic UI tests', () => { 13 | let url; 14 | let browser; 15 | let blockHash; 16 | let topPanel; 17 | let statusPage; 18 | let blockPage; 19 | let trxHash; 20 | 21 | // eslint-disable-next-line no-undef 22 | before(async () => { 23 | browser = await new Builder() 24 | .forBrowser(Browser.CHROME) 25 | .setChromeOptions(new chrome.Options() 26 | .addArguments('--headless') 27 | .windowSize({ 28 | width: 1280, 29 | height: 720, 30 | })) 31 | .build(); 32 | url = 'https://insight.testnet.networks.dash.org/insight/'; 33 | }); 34 | 35 | describe('Home Page', () => { 36 | it('should be able to open main page', async () => { 37 | await browser.get(url); 38 | await wait(10000); 39 | const title = await browser.getTitle(); 40 | expect(title).equal('Home | Insight'); 41 | }); 42 | 43 | it('should be able to open block page', async () => { 44 | topPanel = new TopPanel(browser); 45 | await topPanel.openBlockPage(); 46 | const title = await browser.getTitle(); 47 | expect(title).equal('Home | Insight'); 48 | }); 49 | 50 | it('should be able to open status page', async () => { 51 | await topPanel.openStatusPage(); 52 | await wait(10000); // time to complete sync 53 | const title = await browser.getTitle(); 54 | expect(title).equal('Status | Insight'); 55 | 56 | statusPage = new StatusPage(browser); 57 | 58 | const syncProgress = await statusPage.getSyncProgress(); 59 | expect(syncProgress).equal('100% Complete'); 60 | 61 | const currentSyncStatus = await statusPage.getCurrentSyncStatus(); 62 | expect(currentSyncStatus).equal('finished'); 63 | 64 | const startDate = await statusPage.getStartDate(); 65 | expect(startDate).equal('Invalid date'); 66 | 67 | const initialBlockChainHeight = await statusPage.getInitialBlockChainHeight(); 68 | expect(Number.isInteger(parseInt(initialBlockChainHeight, 10))).equal(true); 69 | 70 | const syncedBlocks = await statusPage.getSyncedBlocks(); 71 | expect(syncedBlocks).equal(''); 72 | 73 | const skippedBlocks = await statusPage.getSkippedBlocks(); 74 | expect(skippedBlocks).equal(''); 75 | 76 | const syncType = await statusPage.getSyncType(); 77 | expect(syncType).equal('bitcore node'); 78 | 79 | const lastBlockHash = await statusPage.getLastBlockHash(); 80 | expect(lastBlockHash).not.equal(undefined); 81 | 82 | const currentBlockchainTip = await statusPage.getCurrentBlockchainTip(); 83 | expect(currentBlockchainTip).not.equal(undefined); 84 | 85 | const version = await statusPage.getVersion(); 86 | expect(Number.isInteger(parseInt(version, 10))).equal(true); 87 | 88 | const protocolVersion = await statusPage.getProtocolVersion(); 89 | expect(Number.isInteger(parseInt(protocolVersion, 10))).equal(true); 90 | 91 | const timeOffset = await statusPage.getTimeOffset(); 92 | expect(timeOffset).equal('0'); 93 | 94 | const miningDifficulty = await statusPage.getMiningDifficulty(); 95 | expect(miningDifficulty).not.equal(''); 96 | 97 | const network = await statusPage.getNetwork(); 98 | expect(network).equal('testnet'); 99 | 100 | const proxySetting = await statusPage.getProxySetting(); 101 | expect(proxySetting).equal(''); 102 | 103 | const infoErrors = await statusPage.getInfoErrors(); 104 | expect(infoErrors).equal(''); 105 | }); 106 | 107 | it('should be able to route to block number', async () => { 108 | const blockIdToSearch = '12'; 109 | 110 | await browser.get(`${url}block/${blockIdToSearch}`); 111 | await wait(10000); 112 | 113 | blockPage = new BlockPage(browser); 114 | 115 | const currentUrl = await browser.getCurrentUrl(); 116 | expect(currentUrl).equal(`${url}block/${blockIdToSearch}`); 117 | 118 | const blockId = (await blockPage.getBlockId()).replace('Block #', ''); 119 | expect(blockId).equal(blockIdToSearch); 120 | blockHash = await blockPage.getBlockHash(); 121 | 122 | const nextBlock = await blockPage.getNextBlock(); 123 | 124 | expect(nextBlock).equal(`${parseInt(blockId, 10) + 1}`); 125 | }); 126 | 127 | it('should be able search by block number', async () => { 128 | const blockIdToSearch = '12'; 129 | topPanel = new TopPanel(browser); 130 | 131 | topPanel.search(blockIdToSearch); 132 | 133 | await wait(10000); 134 | 135 | // init again after page switch 136 | blockPage = new BlockPage(browser); 137 | 138 | // When search from insight search pane, it will redirect to blockHash in url 139 | const currentUrl = await browser.getCurrentUrl(); 140 | expect(currentUrl).equal(`${url}block/${blockHash}`); 141 | 142 | const blockId = (await blockPage.getBlockId()).replace('Block #', ''); 143 | expect(blockId).equal(blockIdToSearch); 144 | blockHash = await blockPage.getBlockHash(); 145 | 146 | const numberOfTrxs = await blockPage.getNumberOfTrxs(); 147 | expect(numberOfTrxs).equal('1'); 148 | 149 | const height = await blockPage.getHeight(); 150 | expect(height).equal(`${blockIdToSearch} (Mainchain)`); 151 | 152 | const blockReward = await blockPage.getBlockReward(); 153 | expect(blockReward).equal('500 DASH'); 154 | 155 | const timestamp = await blockPage.getTimestamp(); 156 | expect(timestamp).not.equal(''); 157 | const minedBy = await blockPage.getMinedBy(); 158 | expect(minedBy).equal(''); 159 | const merkleRoot = await blockPage.getMerkleRoot(); 160 | expect(merkleRoot).not.equal(''); 161 | const previousBlock = await blockPage.getPreviousBlock(); 162 | expect(previousBlock).equal(`${parseInt(blockId, 10) - 1}`); 163 | const difficulty = await blockPage.getDifficulty(); 164 | expect(difficulty).not.equal(''); 165 | const bits = await blockPage.getBits(); 166 | expect(bits).not.equal(''); 167 | const size = await blockPage.getSize(); 168 | expect(size).not.equal(''); 169 | const version = await blockPage.getVersion(); 170 | expect(Number.isInteger(parseInt(version, 10))).equal(true); 171 | const nonce = await blockPage.getNonce(); 172 | expect(Number.isInteger(parseInt(nonce, 10))).equal(true); 173 | const nextBlock = await blockPage.getNextBlock(); 174 | 175 | expect(nextBlock).equal(`${parseInt(blockId, 10) + 1}`); 176 | trxHash = await blockPage.getTrxHash(); 177 | }); 178 | 179 | it('should be able search by block hash', async () => { 180 | const blockIdToSearch = '12'; 181 | topPanel.search(blockHash); 182 | const currentUrl = await browser.getCurrentUrl(); 183 | expect(currentUrl).equal(`${url}block/${blockHash}`); 184 | 185 | const blockId = (await blockPage.getBlockId()).replace('Block #', ''); 186 | expect(blockId).equal(blockIdToSearch); 187 | expect(await blockPage.getBlockHash()).equal(blockHash); 188 | 189 | const numberOfTrxs = await blockPage.getNumberOfTrxs(); 190 | expect(numberOfTrxs).equal('1'); 191 | 192 | const height = await blockPage.getHeight(); 193 | expect(height).equal(`${blockIdToSearch} (Mainchain)`); 194 | 195 | const blockReward = await blockPage.getBlockReward(); 196 | expect(blockReward).equal('500 DASH'); 197 | 198 | const timestamp = await blockPage.getTimestamp(); 199 | expect(timestamp).not.equal(''); 200 | const minedBy = await blockPage.getMinedBy(); 201 | expect(minedBy).equal(''); 202 | const merkleRoot = await blockPage.getMerkleRoot(); 203 | expect(merkleRoot).not.equal(''); 204 | const previousBlock = await blockPage.getPreviousBlock(); 205 | expect(previousBlock).equal(`${parseInt(blockId, 10) - 1}`); 206 | const difficulty = await blockPage.getDifficulty(); 207 | expect(difficulty).not.equal(''); 208 | const bits = await blockPage.getBits(); 209 | expect(bits).not.equal(''); 210 | const size = await blockPage.getSize(); 211 | expect(size).not.equal(''); 212 | const version = await blockPage.getVersion(); 213 | expect(Number.isInteger(parseInt(version, 10))).equal(true); 214 | const nonce = await blockPage.getNonce(); 215 | expect(Number.isInteger(parseInt(nonce, 10))).equal(true); 216 | const nextBlock = await blockPage.getNextBlock(); 217 | expect(nextBlock).equal(`${parseInt(blockId, 10) + 1}`); 218 | 219 | expect(await blockPage.getTrxHash()).equal(trxHash); 220 | }); 221 | }); 222 | }); 223 | --------------------------------------------------------------------------------