├── .eslintrc.yaml ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── assets ├── footer.css └── thumb.svg ├── issues.png ├── lib ├── index.js ├── qrcode.js └── template.hbs ├── package.json ├── page-footer-symmetrical.png └── page-footer.png /.eslintrc.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | root : true 4 | extends : aleen42 5 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | patreon: aleen42 3 | custom: ["http://paypal.me/aleen42", "https://www.buymeacoffee.com/aleen42"] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | # IDEA 36 | .idea 37 | 38 | # locked dependencies 39 | package-lock.json 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5" 4 | - "5.1" 5 | - "4" 6 | - "4.2" 7 | - "4.1" 8 | - "4.0" 9 | - "0.12" 10 | - "0.11" 11 | - "0.10" 12 | - "iojs" 13 | script: make test 14 | os: 15 | - linux -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Aleen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## page-footer 2 | 3 | ![gitbook](https://badges.aleen42.com/src/gitbook_1.svg) ![gitbook](https://badges.aleen42.com/src/gitbook_2.svg) 4 | 5 | [![npm](https://img.shields.io/npm/v/gitbook-plugin-page-footer.svg)](https://www.npmjs.com/package/gitbook-plugin-page-footer) [![Build Status](https://travis-ci.org/aleen42/gitbook-footer.svg?branch=master)](https://travis-ci.org/aleen42/gitbook-footer) [![devDependency Status](https://david-dm.org/aleen42/gitbook-footer/dev-status.svg)](https://david-dm.org/aleen42/gitbook-footer#info=devDependencies) [![npm](https://img.shields.io/npm/dt/gitbook-plugin-page-footer.svg)](https://www.npmjs.com/package/gitbook-plugin-page-footer) 6 | 7 | A GitBook plugin to show issues and some other information of the repository behind each page. 8 | 9 | ### Normal Style 10 | 11 | ![page-footer](./page-footer.png) 12 | 13 | ### Symmetrical Style 14 | 15 | ![page-footer](./page-footer-symmetrical.png) 16 | 17 | ### Issues Part 18 | 19 | In this part, you can specify a repository and show your latest 8 opened issues or pull requests in a book: 20 | 21 | *Notice that: because there is a rate-limiting for calling GitHub API, so it's suggested to generate a token following the [site](https://github.com/blog/1509-personal-api-tokens). In addition, you can only update this part after rebuilding your books!* 22 | 23 | ![page-footer](./issues.png) 24 | 25 | ### Installation 26 | 27 | Add the following plugins to your `book.json` and run `gitbook install` 28 | 29 | ```json 30 | { 31 | "plugins": ["page-footer"] 32 | } 33 | ``` 34 | 35 | ### Usage 36 | 37 | Just find the plugin on gitbook and install it on your gitbook project. 38 | 39 | The default configuration is: 40 | 41 | ```json 42 | { 43 | "plugins": [ 44 | "page-footer" 45 | ], 46 | "pluginsConfig": { 47 | "page-footer": { 48 | "description": "modified at", 49 | "signature": "Aleen", 50 | "wisdom": "More than a coder, more than a designer", 51 | "format": "yyyy-MM-dd hh:mm:ss", 52 | "copyright": "Copyright © aleen42", 53 | "timeColor": "#666", 54 | "copyrightColor": "#666", 55 | "utcOffset": "8", 56 | "isShowQRCode": true, 57 | "isShowIssues": true, 58 | "baseUri": "https://wiki.aleen42.com/", 59 | "repo": "aleen42/PersonalWiki", 60 | "issueNum": "8", 61 | "token": "", 62 | "style": "normal" 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | ### Release History 69 | 70 | * ==================== **1.0.0 Initial release** ==================== 71 | * 1.0.1 fix bugs 72 | * 1.0.2 fix bugs 73 | * 1.0.3 fix bugs 74 | * 1.0.4 fix bugs 75 | * 1.0.5 fix bugs 76 | * 1.0.6 update readme 77 | * 1.0.7 update package.json 78 | * 1.0.8 update readme 79 | * 1.0.9 update readme 80 | * 1.1.0 version restrict 81 | * 1.1.1 configuration 82 | * 1.1.2 configuration 83 | * 1.1.3 fix bugs 84 | * 1.1.4 fix bugs 85 | * 1.1.5 fix bugs 86 | * 1.1.6 fix bugs 87 | * 1.1.7 fix bugs 88 | * 1.1.8 fix bugs 89 | * 1.1.9 debug mode 90 | * 1.2.0 debug mode 91 | * 1.2.1 debug mode 92 | * 1.2.2 debug mode 93 | * 1.2.3 debug mode 94 | * 1.2.4 debug mode 95 | * ================ **1.3.3 released version** ================ 96 | * 1.3.4 add title 97 | * ================ **1.4.3 released version** ================ 98 | * ================ **1.4.6 released version** ================ 99 | * 1.4.8 update readme 100 | * 1.4.9 update readme 101 | * ==================== **2.0.0 Featuring configuration** ==================== 102 | * 2.0.1 update readme 103 | * 2.0.2 update dependency version 104 | * 2.0.3 update readme 105 | * 2.0.4 update readme 106 | * 2.0.5 update readme 107 | * 2.0.6 update readme 108 | * 2.0.7 update readme 109 | * 2.0.8 update readme 110 | * 2.0.9 update readme 111 | * 2.1.0 update readme 112 | * 2.1.1 update style 113 | * 2.1.2 unused version 114 | * 2.1.3 featuring Timezone 115 | * 2.1.4 fix bugs 116 | * 2.1.5 fix bugs 117 | * 2.1.6 fix bugs 118 | * 2.1.7 update readme 119 | * 2.8.9 unused version 120 | * ==================== **3.0.0 Featuring Qrcode** ==================== 121 | * 3.0.1 update style 122 | * 3.0.6 fix bugs 123 | * 3.0.7 justify style for mobile 124 | * 3.0.8 justify style for mobile 125 | * 3.0.9 support optional styles 126 | * 3.1.0 featuring style of symmetrical 127 | * 3.1.1 fix bugs 128 | * 3.1.2 fix bugs 129 | * 3.1.3 fix bugs 130 | * 3.1.4 fix bugs 131 | * ==================== **4.0.0 Featuring Optional Styles** ==================== 132 | * 4.0.1 modify description of options 133 | * 4.0.2 change qrcode's quality and size 134 | * 4.0.3 change stylesheet 135 | * 4.0.4 change stylesheet 136 | * 4.0.5 change stylesheet 137 | * 4.0.6 change stylesheet 138 | * 4.0.7 hotfix 139 | * 4.0.8 hotfix 140 | * 4.0.9 update readme 141 | * 4.1.9 update readme 142 | * 4.2.9 fix bugs of Gitbook engine 3.0.3 143 | * 4.3.0 update readme 144 | * ==================== **5.0.0 Featuring Issues Style** ==================== 145 | * 5.0.1 update readme 146 | * 5.0.2 update default config 147 | * 5.0.4 fix bugs of issues [#4](https://github.com/aleen42/gitbook-footer/issues/4) 148 | * 5.0.5 update readme 149 | * 5.0.6 fix bugs 150 | * 5.0.7 fix bugs of issues [#5](https://github.com/aleen42/gitbook-footer/issues/5) 151 | * 5.0.8 update readme 152 | * 5.0.9 fix bugs 153 | * 5.1.0 fix bugs 154 | * 5.1.1 update styles 155 | * 5.1.2 update styles 156 | * 5.1.3 update styles 157 | * 5.1.4 update readme 158 | * 5.1.5 update readme 159 | * 5.1.6 optional issue number 160 | * 5.1.7 restrict reading book 161 | * 5.1.9 update default value 162 | * 5.2.0 fix bugs 163 | * 5.2.1 fix bugs 164 | * 5.2.2 merge pull requests of [#6](https://github.com/aleen42/gitbook-footer/issues/6) 165 | * 5.2.3 update test cases 166 | * 5.2.4 update readme 167 | * 5.2.5 update readme 168 | * 5.2.6 update code 169 | * 5.2.7 update readme 170 | * 5.3.0 calculate a proper color for the text in labels 171 | * 5.3.1 update readme 172 | * 5.3.2 fix timezone problem of UTC 173 | * 5.3.3 support GITHUB_TOKEN under travis-ci building 174 | * 5.3.4 fix the number of issues 175 | * 5.3.5 fix the problem of [#9](https://github.com/aleen42/gitbook-footer/issues/9) 176 | * 5.3.6 fix style and extend super option 177 | * 5.3.8 enhancement of styles 178 | * 5.3.9 fix style of normal type 179 | * 5.4.0 remove style of QRCode within normal type 180 | * 5.4.1 compatible style for gitbook themes 181 | * 5.4.3 deprecated token access way for GitHub 182 | * 5.4.7 use gitbook-color to support light or dark theme 183 | * 5.4.8 refactor 184 | * 5.4.9 fix the dependency problem [#13](https://github.com/aleen42/gitbook-footer/issues/13) 185 | * 5.5.0 eliminate different sizes of labels when changing color mode 186 | 187 | ### :fuelpump: How to contribute 188 | 189 | Have an idea? Found a bug? See [how to contribute](https://wiki.aleen42.com/contribution.html). 190 | 191 | ### :scroll: License 192 | 193 | [MIT](https://wiki.aleen42.com/MIT.html) © aleen42 194 | 195 | *Note: if you like this project, feel free to buy me a swimming chance:* 196 | 197 | [![badges](https://badges.aleen42.com/src/paypal.svg)](http://paypal.me/aleen42) [![badges](https://badges.aleen42.com/src/patreon.svg)](https://www.patreon.com/aleen42) [![badges](https://badges.aleen42.com/src/buymeacoffee.svg)](https://www.buymeacoffee.com/aleen42) 198 | -------------------------------------------------------------------------------- /assets/footer.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Common Parts 3 | */ 4 | 5 | .footer { 6 | margin-top: 50px; 7 | padding: 10px 0 80px; 8 | font-size: 12px; 9 | position: relative 10 | } 11 | 12 | .paragraph { 13 | margin: 0 !important; 14 | line-height: normal !important; 15 | } 16 | 17 | .super { 18 | font-size: 0.8em !important; 19 | margin-left: 5px !important; 20 | } 21 | 22 | .box__issues { 23 | margin-top: 50px !important; 24 | text-align: center !important; 25 | } 26 | 27 | .issue-line { 28 | width: 50%; 29 | height: 0; 30 | display: inline-block; 31 | border-bottom: 1px solid var(--color-book-border, rgba(255, 255, 255, 0.2)); 32 | text-align: center; 33 | box-sizing: border-box; 34 | margin: 0 auto; 35 | } 36 | 37 | .issue-header { 38 | font-size: 1.4em; 39 | height: 1.4em; 40 | position: relative; 41 | top: -0.7em; 42 | display: inline-block; 43 | padding: 0 10px; 44 | margin: 0 auto; 45 | background-color: var(--color-bg-primary, #fff); 46 | } 47 | 48 | .issue-label { 49 | float: right; 50 | padding: 0 3px !important; 51 | border-radius: 5px; 52 | margin-left: 10px; 53 | } 54 | 55 | .issue-label { 56 | --lightness-threshold: 0.453; 57 | --border-threshold: 0.96; 58 | --border-alpha: max(0,min(calc(var(--perceived-lightness)*100 - var(--border-threshold)*100),1)); 59 | background: rgb(var(--label-r),var(--label-g),var(--label-b)); 60 | border: 1px solid hsla(var(--label-h),calc(var(--label-s)*1%),calc((var(--label-l) - 25)*1%),var(--border-alpha)); 61 | color: hsl(0,0%,calc(var(--lightness-switch)*100%)); 62 | --perceived-lightness: calc(var(--label-r) * 0.2126 / 255 + var(--label-g) * 0.7152 / 255 + var(--label-b) * 0.0722 / 255); 63 | --lightness-switch: max(0, min(calc(var(--perceived-lightness) * -1000 - var(--lightness-threshold) * -1000), 1)); 64 | } 65 | 66 | [data-color-mode=dark] .issue-label { 67 | --lightness-threshold: 0.6; 68 | --background-alpha: 0.18; 69 | --border-alpha: 0.3; 70 | --lighten-by: calc((var(--lightness-threshold) * 100 - var(--perceived-lightness) * 100) * var(--lightness-switch)); 71 | background: rgba(var(--label-r), var(--label-g), var(--label-b), var(--background-alpha)); 72 | border: 1px solid hsla(var(--label-h), calc(var(--label-s) * 1%), calc((var(--label-l) + var(--lighten-by)) * 1%), var(--border-alpha)); 73 | color: hsl(var(--label-h), calc(var(--label-s) * 1%), calc((var(--label-l) + var(--lighten-by)) * 1%)); 74 | } 75 | 76 | .issue-edge { 77 | border-top: 1px solid var(--color-book-border, rgba(255, 255, 255, 0.2)); 78 | } 79 | 80 | .issues { 81 | margin: 0 !important; 82 | text-align: left; 83 | } 84 | 85 | @media screen and (min-width: 720px) { 86 | .issue-edge { 87 | width: 60%; 88 | margin: 0 0 auto 100px !important; 89 | } 90 | 91 | .issues { 92 | padding: 10px 60px !important; 93 | } 94 | } 95 | 96 | @media screen and (max-width: 720px) { 97 | .issue-edge { 98 | width: 30%; 99 | margin: 0 0 auto 20px !important; 100 | } 101 | 102 | .issues { 103 | padding: 10px !important; 104 | } 105 | } 106 | 107 | /** 108 | * 109 | * 110 | * 111 | * Normal Style 112 | * 113 | * 114 | * 115 | * 116 | */ 117 | 118 | .footer__container--normal { 119 | display: flex !important; 120 | } 121 | 122 | .footer__description--normal { 123 | margin: 0 10px !important; 124 | vertical-align: top !important; 125 | flex: 1; 126 | position: relative; 127 | } 128 | 129 | .footer__author--normal { 130 | font-size: 2em !important; 131 | border-bottom: 1px solid var(--color-book-border, rgba(255, 255, 255, 0.2)); 132 | padding-bottom: 10px; 133 | } 134 | 135 | .footer__quote--normal { 136 | word-break: break-all; 137 | padding: 10px 0; 138 | } 139 | 140 | .footer__modifyTime--normal { 141 | float: right; 142 | } 143 | 144 | .footer__main--normal { 145 | float: right; 146 | font-size: 12px !important; 147 | } 148 | 149 | .footer__main__paragraph--normal { 150 | margin: 3px 0 !important; 151 | } 152 | 153 | /** 154 | * 155 | * 156 | * 157 | * Symmetrical Style 158 | * 159 | * 160 | * 161 | * 162 | */ 163 | 164 | .footer__container--symmetrical { 165 | text-align: center !important; 166 | } 167 | 168 | .footer__description--symmetrical { 169 | padding: 0 10px !important; 170 | vertical-align: top !important; 171 | flex: 1; 172 | } 173 | 174 | .footer__author--symmetrical { 175 | font-size: 2em !important; 176 | padding-left: 0.8em !important; 177 | border-bottom: 1px solid var(--color-book-border, rgba(255, 255, 255, 0.2)); 178 | padding-bottom: 10px; 179 | } 180 | 181 | .footer__modifyTime--symmetrical { 182 | float: right; 183 | } 184 | 185 | @media screen and (min-width: 720px) { 186 | /* styles applied when the page/app is viewed on a screen with a width greater than or equal to 720px */ 187 | .footer__quote--symmetrical { 188 | margin-top: 10px !important; 189 | } 190 | .footer__main--symmetrical { 191 | position: absolute; 192 | right: 0; 193 | font-size: 12px !important; 194 | text-align: right; 195 | bottom: 0; 196 | } 197 | } 198 | 199 | @media screen and (max-width: 720px) { 200 | /* styles applied when the page/app is viewed on a screen with a width greater than or equal to 720px */ 201 | .footer__quote--symmetrical { 202 | display: none; 203 | } 204 | .footer__main--symmetrical { 205 | font-size: 12px !important; 206 | margin-top: 10px !important; 207 | text-align: right; 208 | } 209 | } 210 | 211 | .footer__main__paragraph--symmetrical { 212 | margin: 3px 0 !important; 213 | } 214 | 215 | .footer svg > path { 216 | fill: var(--color-text-primary, #24292f); 217 | } 218 | -------------------------------------------------------------------------------- /assets/thumb.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 26 | 27 | -------------------------------------------------------------------------------- /issues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aleen42/gitbook-footer/e8035d7fe0e28da99e4f62aa517f665f7f64dcb1/issues.png -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | * _ 3 | * _____ _ ____ _ |_| 4 | * | _ |/ \ ____ ____ __ ___ / ___\/ \ __ _ ____ _ 5 | * | |_| || | / __ \/ __ \\ '_ \ _ / / | |___\ \ | |/ __ \| | 6 | * | _ || |__. ___/. ___/| | | ||_|\ \___ | _ | |_| |. ___/| | 7 | * |_/ \_|\___/\____|\____||_| |_| \____/|_| |_|_____|\____||_| 8 | * 9 | * ================================================================ 10 | * More than a coder, More than a designer 11 | * ================================================================ 12 | * 13 | * 14 | * - Document: index.js 15 | * - Author: aleen42 16 | * - Description: the main entrance for page-footer 17 | * - Create Time: Apr 16th, 2016 18 | * - Update Time: Jul 26th, 2021 19 | * 20 | * 21 | **********************************************************************/ 22 | 23 | const fs = require('fs'); 24 | const path = require('path'); 25 | const Handlebars = require('handlebars'); 26 | const tplStr = fs.readFileSync(path.resolve(__dirname, './template.hbs'), 'utf8'); 27 | 28 | /** 29 | * [request: the request module] 30 | * @type {[type]} 31 | */ 32 | const syncReq = require('sync-request'); 33 | 34 | /** 35 | * [request: the request module] 36 | * @type {[type]} 37 | */ 38 | const NodeCache = require('node-cache'); 39 | const localCache = new NodeCache({}); 40 | 41 | /** include qrcode.js */ 42 | const qrcode = require('./qrcode.js'); 43 | 44 | /** set Date protocol */ 45 | // eslint-disable-next-line no-extend-native 46 | Date.prototype.format = function (format) { 47 | const date = { 48 | 'M+': this.getMonth() + 1, 49 | 'd+': this.getDate(), 50 | 'h+': this.getHours(), 51 | 'm+': this.getMinutes(), 52 | 's+': this.getSeconds(), 53 | 'q+': Math.floor((this.getMonth() + 3) / 3), 54 | 'S+': this.getMilliseconds(), 55 | }; 56 | 57 | if (/(y+)/i.test(format)) { 58 | format = format.replace(RegExp.$1, `${this.getFullYear()}`.substr(4 - RegExp.$1.length)); 59 | } 60 | 61 | Object.entries(date).forEach(([k, v]) => { 62 | if (new RegExp(`(${k})`).test(format)) { 63 | format = format.replace(RegExp.$1, RegExp.$1.length === 1 ? v : `00${v}`.substr(`${v}`.length)); 64 | } 65 | }); 66 | return format; 67 | }; 68 | 69 | /** 70 | * [defaultOption: default option] 71 | * @type {Object} 72 | */ 73 | const defaultOption = { 74 | description: 'modified at', 75 | signature: 'Aleen', 76 | wisdom: 'More than a coder, more than a designer', 77 | format: 'yyyy-MM-dd hh:mm:ss', 78 | copyright: 'Copyright © aleen42', 79 | timeColor: '#666', 80 | copyrightColor: '#666', 81 | utcOffset: '8', 82 | isShowQRCode: true, 83 | baseUri: 'https://aleen42.gitbooks.io/personalwiki/content/', 84 | isShowIssues: true, 85 | repo: 'aleen42/PersonalWiki', 86 | issueNum: '8', 87 | style: 'normal', 88 | super: '®', 89 | }; 90 | 91 | // noinspection JSUnusedGlobalSymbols 92 | /** 93 | * [main module] 94 | * @type {Object} 95 | */ 96 | module.exports = { 97 | generate, 98 | /** Map of new style */ 99 | book: { 100 | assets: './assets', 101 | css: ['footer.css'], 102 | }, 103 | 104 | /** Map of hooks */ 105 | hooks: { 106 | 'page:before': function (page) { 107 | /** add contents to the original content */ 108 | if (this.output.name === 'website') { 109 | page.content = page.content + generate(this.config.get('pluginsConfig')['page-footer']); 110 | } 111 | 112 | return page; 113 | }, 114 | }, 115 | 116 | /** Map of new filters */ 117 | filters: { 118 | dateFormat: function (d, format, utc) { 119 | let reservedDate = new Date(d); 120 | /** convert to UTC firstly */ 121 | reservedDate = new Date( 122 | reservedDate.getUTCFullYear(), 123 | reservedDate.getUTCMonth(), 124 | reservedDate.getUTCDate(), 125 | reservedDate.getUTCHours(), 126 | reservedDate.getUTCMinutes(), 127 | reservedDate.getUTCSeconds(), 128 | ); 129 | reservedDate.setTime(reservedDate.getTime() + (!utc ? 8 : parseInt(utc)) * 60 * 60 * 1000); 130 | return reservedDate.format(format); 131 | }, 132 | 133 | currentURI: function (d, baseUri) { 134 | // noinspection JSUnresolvedFunction 135 | return this.output.name === 'website' ? createQRCode(baseUri + this.output.toURL(d), 15, 'Q') : ''; 136 | }, 137 | }, 138 | }; 139 | 140 | function hex2rgb(hex) { 141 | return Array(3).fill('').map((_, i) => parseInt(hex.slice(2 * i, 2 * (i + 1)), 16)); 142 | } 143 | 144 | function rgb2hsl(r, g, b) { 145 | r /= 255; 146 | g /= 255; 147 | b /= 255; 148 | const max = Math.max(r, g, b); 149 | const min = Math.min(r, g, b); 150 | let h = 0; 151 | let s; 152 | let l = (max + min) / 2; 153 | if (max === min) { 154 | h = s = 0; 155 | } else { 156 | const d = max - min; 157 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min); 158 | switch (max) { 159 | case r: 160 | h = (g - b) / d + (g < b ? 6 : 0); 161 | break; 162 | case g: 163 | h = (b - r) / d + 2; 164 | break; 165 | case b: 166 | h = (r - g) / d + 4; 167 | } 168 | h /= 6; 169 | } 170 | h = h * 360; 171 | s = s * 100; 172 | l = l * 100; 173 | return [h, s, l]; 174 | } 175 | 176 | function createQRCode(text, typeNumber, errorCorrectLevel) { 177 | const qr = qrcode(typeNumber || 10, errorCorrectLevel || 'H'); 178 | qr.addData(text); 179 | qr.make(); 180 | 181 | return qr.createSvgTag(); 182 | } 183 | 184 | function generate(configs) { 185 | const options = { 186 | ...defaultOption, 187 | ...configs, 188 | copyright: `${configs.copyright || defaultOption.copyright} all right reserved, powered by aleen42`, 189 | }; 190 | 191 | return Handlebars.compile(tplStr)({ 192 | ...options, 193 | style: /normal|symmetrical/.test(options.style) ? options.style : 'normal', 194 | issues : options.isShowIssues && listIssues(options), 195 | }); 196 | } 197 | 198 | function listIssues({ token: configToken, repo, format, utcOffset, issueNum }) { 199 | const token = configToken || process.env.ACCESS_TOKEN; 200 | 201 | /** clear cache at the first time */ 202 | if (localCache.get('cleared') !== 'true') { 203 | localCache.del('issues'); 204 | localCache.set('cleared', 'true'); 205 | } 206 | 207 | const result = localCache.get('issues') || (() => { 208 | const UA = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.75 Safari/537.36'; 209 | const res = syncReq('GET', `https://api.github.com/repos/${repo}/issues?per_page=${issueNum}`, { 210 | headers: { 211 | 'user-agent': UA, 212 | ...token && { Authorization: `token ${token}` }, 213 | }, 214 | }); 215 | 216 | if (res.statusCode === 200) { 217 | // noinspection JSUnresolvedFunction 218 | localCache.set('issues', res.getBody().toString()); 219 | return localCache.get('issues'); 220 | } 221 | })(); 222 | 223 | if (!result) return ''; 224 | 225 | /** parse json */ 226 | const issues = JSON.parse(result); 227 | return `

${issues.length} issues reported

` 228 | + issues.map(({ labels, updated_at: updated, html_url: url, title, number }) => { 229 | const date = new Date(updated); 230 | date.setTime(date.getTime() + (isNaN(parseInt(utcOffset)) ? 20 : parseInt(utcOffset)) * 60 * 60 * 1000); 231 | 232 | return `

#${number} ${title}${date.format(format)}${(labels.map(label => { 233 | const rgb = hex2rgb(label.color); 234 | const r = rgb[0], g = rgb[1], b = rgb[2]; 235 | const hsl = rgb2hsl(r, g, b); 236 | const h = hsl[0], s = hsl[1], l = hsl[2]; 237 | 238 | return `${label.name}`; 241 | }).join(''))}

`; 242 | }).join('

'); 243 | } 244 | -------------------------------------------------------------------------------- /lib/qrcode.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // 3 | // QR Code Generator for JavaScript 4 | // 5 | // Copyright (c) 2009 Kazuhiko Arase 6 | // 7 | // URL: http://www.d-project.com/ 8 | // 9 | // Licensed under the MIT license: 10 | // http://www.opensource.org/licenses/mit-license.php 11 | // 12 | // The word 'QR Code' is registered trademark of 13 | // DENSO WAVE INCORPORATED 14 | // http://www.denso-wave.com/qrcode/faqpatent-e.html 15 | // 16 | //--------------------------------------------------------------------- 17 | 18 | module.exports = function() { 19 | //--------------------------------------------------------------------- 20 | // qrcode 21 | //--------------------------------------------------------------------- 22 | 23 | /** 24 | * qrcode 25 | * @param typeNumber 1 to 40 26 | * @param errorCorrectLevel 'L','M','Q','H' 27 | */ 28 | var qrcode = function(typeNumber, errorCorrectLevel) { 29 | 30 | var PAD0 = 0xEC; 31 | var PAD1 = 0x11; 32 | 33 | var _typeNumber = typeNumber; 34 | var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel]; 35 | var _modules = null; 36 | var _moduleCount = 0; 37 | var _dataCache = null; 38 | var _dataList = new Array(); 39 | 40 | var _this = {}; 41 | 42 | var makeImpl = function(test, maskPattern) { 43 | 44 | _moduleCount = _typeNumber * 4 + 17; 45 | _modules = function(moduleCount) { 46 | var modules = new Array(moduleCount); 47 | for (var row = 0; row < moduleCount; row += 1) { 48 | modules[row] = new Array(moduleCount); 49 | for (var col = 0; col < moduleCount; col += 1) { 50 | modules[row][col] = null; 51 | } 52 | } 53 | return modules; 54 | }(_moduleCount); 55 | 56 | setupPositionProbePattern(0, 0); 57 | setupPositionProbePattern(_moduleCount - 7, 0); 58 | setupPositionProbePattern(0, _moduleCount - 7); 59 | setupPositionAdjustPattern(); 60 | setupTimingPattern(); 61 | setupTypeInfo(test, maskPattern); 62 | 63 | if (_typeNumber >= 7) { 64 | setupTypeNumber(test); 65 | } 66 | 67 | if (_dataCache == null) { 68 | _dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList); 69 | } 70 | 71 | mapData(_dataCache, maskPattern); 72 | }; 73 | 74 | var setupPositionProbePattern = function(row, col) { 75 | 76 | for (var r = -1; r <= 7; r += 1) { 77 | 78 | if (row + r <= -1 || _moduleCount <= row + r) continue; 79 | 80 | for (var c = -1; c <= 7; c += 1) { 81 | 82 | if (col + c <= -1 || _moduleCount <= col + c) continue; 83 | 84 | if ((0 <= r && r <= 6 && (c == 0 || c == 6)) || (0 <= c && c <= 6 && (r == 0 || r == 6)) || (2 <= r && r <= 4 && 2 <= c && c <= 4)) { 85 | _modules[row + r][col + c] = true; 86 | } else { 87 | _modules[row + r][col + c] = false; 88 | } 89 | } 90 | } 91 | }; 92 | 93 | var getBestMaskPattern = function() { 94 | 95 | var minLostPoint = 0; 96 | var pattern = 0; 97 | 98 | for (var i = 0; i < 8; i += 1) { 99 | 100 | makeImpl(true, i); 101 | 102 | var lostPoint = QRUtil.getLostPoint(_this); 103 | 104 | if (i == 0 || minLostPoint > lostPoint) { 105 | minLostPoint = lostPoint; 106 | pattern = i; 107 | } 108 | } 109 | 110 | return pattern; 111 | }; 112 | 113 | var setupTimingPattern = function() { 114 | 115 | for (var r = 8; r < _moduleCount - 8; r += 1) { 116 | if (_modules[r][6] != null) { 117 | continue; 118 | } 119 | _modules[r][6] = (r % 2 == 0); 120 | } 121 | 122 | for (var c = 8; c < _moduleCount - 8; c += 1) { 123 | if (_modules[6][c] != null) { 124 | continue; 125 | } 126 | _modules[6][c] = (c % 2 == 0); 127 | } 128 | }; 129 | 130 | var setupPositionAdjustPattern = function() { 131 | 132 | var pos = QRUtil.getPatternPosition(_typeNumber); 133 | 134 | for (var i = 0; i < pos.length; i += 1) { 135 | 136 | for (var j = 0; j < pos.length; j += 1) { 137 | 138 | var row = pos[i]; 139 | var col = pos[j]; 140 | 141 | if (_modules[row][col] != null) { 142 | continue; 143 | } 144 | 145 | for (var r = -2; r <= 2; r += 1) { 146 | 147 | for (var c = -2; c <= 2; c += 1) { 148 | 149 | if (r == -2 || r == 2 || c == -2 || c == 2 || (r == 0 && c == 0)) { 150 | _modules[row + r][col + c] = true; 151 | } else { 152 | _modules[row + r][col + c] = false; 153 | } 154 | } 155 | } 156 | } 157 | } 158 | }; 159 | 160 | var setupTypeNumber = function(test) { 161 | 162 | var bits = QRUtil.getBCHTypeNumber(_typeNumber); 163 | 164 | for (var i = 0; i < 18; i += 1) { 165 | var mod = (!test && ((bits >> i) & 1) == 1); 166 | _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; 167 | } 168 | 169 | for (var i = 0; i < 18; i += 1) { 170 | var mod = (!test && ((bits >> i) & 1) == 1); 171 | _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; 172 | } 173 | }; 174 | 175 | var setupTypeInfo = function(test, maskPattern) { 176 | 177 | var data = (_errorCorrectLevel << 3) | maskPattern; 178 | var bits = QRUtil.getBCHTypeInfo(data); 179 | 180 | // vertical 181 | for (var i = 0; i < 15; i += 1) { 182 | 183 | var mod = (!test && ((bits >> i) & 1) == 1); 184 | 185 | if (i < 6) { 186 | _modules[i][8] = mod; 187 | } else if (i < 8) { 188 | _modules[i + 1][8] = mod; 189 | } else { 190 | _modules[_moduleCount - 15 + i][8] = mod; 191 | } 192 | } 193 | 194 | // horizontal 195 | for (var i = 0; i < 15; i += 1) { 196 | 197 | var mod = (!test && ((bits >> i) & 1) == 1); 198 | 199 | if (i < 8) { 200 | _modules[8][_moduleCount - i - 1] = mod; 201 | } else if (i < 9) { 202 | _modules[8][15 - i - 1 + 1] = mod; 203 | } else { 204 | _modules[8][15 - i - 1] = mod; 205 | } 206 | } 207 | 208 | // fixed module 209 | _modules[_moduleCount - 8][8] = (!test); 210 | }; 211 | 212 | var mapData = function(data, maskPattern) { 213 | 214 | var inc = -1; 215 | var row = _moduleCount - 1; 216 | var bitIndex = 7; 217 | var byteIndex = 0; 218 | var maskFunc = QRUtil.getMaskFunction(maskPattern); 219 | 220 | for (var col = _moduleCount - 1; col > 0; col -= 2) { 221 | 222 | if (col == 6) col -= 1; 223 | 224 | while (true) { 225 | 226 | for (var c = 0; c < 2; c += 1) { 227 | 228 | if (_modules[row][col - c] == null) { 229 | 230 | var dark = false; 231 | 232 | if (byteIndex < data.length) { 233 | dark = (((data[byteIndex] >>> bitIndex) & 1) == 1); 234 | } 235 | 236 | var mask = maskFunc(row, col - c); 237 | 238 | if (mask) { 239 | dark = !dark; 240 | } 241 | 242 | _modules[row][col - c] = dark; 243 | bitIndex -= 1; 244 | 245 | if (bitIndex == -1) { 246 | byteIndex += 1; 247 | bitIndex = 7; 248 | } 249 | } 250 | } 251 | 252 | row += inc; 253 | 254 | if (row < 0 || _moduleCount <= row) { 255 | row -= inc; 256 | inc = -inc; 257 | break; 258 | } 259 | } 260 | } 261 | }; 262 | 263 | var createBytes = function(buffer, rsBlocks) { 264 | 265 | var offset = 0; 266 | 267 | var maxDcCount = 0; 268 | var maxEcCount = 0; 269 | 270 | var dcdata = new Array(rsBlocks.length); 271 | var ecdata = new Array(rsBlocks.length); 272 | 273 | for (var r = 0; r < rsBlocks.length; r += 1) { 274 | 275 | var dcCount = rsBlocks[r].dataCount; 276 | var ecCount = rsBlocks[r].totalCount - dcCount; 277 | 278 | maxDcCount = Math.max(maxDcCount, dcCount); 279 | maxEcCount = Math.max(maxEcCount, ecCount); 280 | 281 | dcdata[r] = new Array(dcCount); 282 | 283 | for (var i = 0; i < dcdata[r].length; i += 1) { 284 | dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; 285 | } 286 | offset += dcCount; 287 | 288 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); 289 | var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1); 290 | 291 | var modPoly = rawPoly.mod(rsPoly); 292 | ecdata[r] = new Array(rsPoly.getLength() - 1); 293 | for (var i = 0; i < ecdata[r].length; i += 1) { 294 | var modIndex = i + modPoly.getLength() - ecdata[r].length; 295 | ecdata[r][i] = (modIndex >= 0) ? modPoly.getAt(modIndex) : 0; 296 | } 297 | } 298 | 299 | var totalCodeCount = 0; 300 | for (var i = 0; i < rsBlocks.length; i += 1) { 301 | totalCodeCount += rsBlocks[i].totalCount; 302 | } 303 | 304 | var data = new Array(totalCodeCount); 305 | var index = 0; 306 | 307 | for (var i = 0; i < maxDcCount; i += 1) { 308 | for (var r = 0; r < rsBlocks.length; r += 1) { 309 | if (i < dcdata[r].length) { 310 | data[index] = dcdata[r][i]; 311 | index += 1; 312 | } 313 | } 314 | } 315 | 316 | for (var i = 0; i < maxEcCount; i += 1) { 317 | for (var r = 0; r < rsBlocks.length; r += 1) { 318 | if (i < ecdata[r].length) { 319 | data[index] = ecdata[r][i]; 320 | index += 1; 321 | } 322 | } 323 | } 324 | 325 | return data; 326 | }; 327 | 328 | var createData = function(typeNumber, errorCorrectLevel, dataList) { 329 | 330 | var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); 331 | 332 | var buffer = qrBitBuffer(); 333 | 334 | for (var i = 0; i < dataList.length; i += 1) { 335 | var data = dataList[i]; 336 | buffer.put(data.getMode(), 4); 337 | buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber)); 338 | data.write(buffer); 339 | } 340 | 341 | // calc num max data. 342 | var totalDataCount = 0; 343 | for (var i = 0; i < rsBlocks.length; i += 1) { 344 | totalDataCount += rsBlocks[i].dataCount; 345 | } 346 | 347 | if (buffer.getLengthInBits() > totalDataCount * 8) { 348 | throw new Error('code length overflow. (' + buffer.getLengthInBits() + '>' + totalDataCount * 8 + ')'); 349 | } 350 | 351 | // end code 352 | if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { 353 | buffer.put(0, 4); 354 | } 355 | 356 | // padding 357 | while (buffer.getLengthInBits() % 8 != 0) { 358 | buffer.putBit(false); 359 | } 360 | 361 | // padding 362 | while (true) { 363 | 364 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 365 | break; 366 | } 367 | buffer.put(PAD0, 8); 368 | 369 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 370 | break; 371 | } 372 | buffer.put(PAD1, 8); 373 | } 374 | 375 | return createBytes(buffer, rsBlocks); 376 | }; 377 | 378 | _this.addData = function(data) { 379 | var newData = qr8BitByte(data); 380 | _dataList.push(newData); 381 | _dataCache = null; 382 | }; 383 | 384 | _this.isDark = function(row, col) { 385 | if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) { 386 | throw new Error(row + ',' + col); 387 | } 388 | return _modules[row][col]; 389 | }; 390 | 391 | _this.getModuleCount = function() { 392 | return _moduleCount; 393 | }; 394 | 395 | _this.make = function() { 396 | makeImpl(false, getBestMaskPattern()); 397 | }; 398 | 399 | _this.createTableTag = function(cellSize, margin) { 400 | 401 | cellSize = cellSize || 2; 402 | margin = (typeof margin == 'undefined') ? cellSize * 4 : margin; 403 | 404 | var qrHtml = ''; 405 | 406 | qrHtml += ''; 411 | qrHtml += ''; 412 | 413 | for (var r = 0; r < _this.getModuleCount(); r += 1) { 414 | 415 | qrHtml += ''; 416 | 417 | for (var c = 0; c < _this.getModuleCount(); c += 1) { 418 | qrHtml += ''; 431 | } 432 | 433 | qrHtml += ''; 434 | qrHtml += '
'; 428 | } 429 | 430 | qrHtml += '
'; 435 | 436 | return qrHtml; 437 | }; 438 | 439 | _this.createSvgTag = function(cellSize, margin) { 440 | 441 | cellSize = cellSize || 2; 442 | margin = (typeof margin == 'undefined') ? cellSize * 4 : margin; 443 | var size = _this.getModuleCount() * cellSize + margin * 2; 444 | var c, mc, r, mr, qrSvg = '', 445 | rect; 446 | 447 | rect = 'l' + cellSize + ',0 0,' + cellSize + 448 | ' -' + cellSize + ',0 0,-' + cellSize + 'z '; 449 | 450 | qrSvg += '>> 8); 568 | bytes.push(b & 0xff); 569 | } 570 | } else { 571 | bytes.push(unknownChar); 572 | } 573 | } 574 | } 575 | return bytes; 576 | }; 577 | }; 578 | 579 | //--------------------------------------------------------------------- 580 | // QRMode 581 | //--------------------------------------------------------------------- 582 | 583 | var QRMode = { 584 | MODE_NUMBER: 1 << 0, 585 | MODE_ALPHA_NUM: 1 << 1, 586 | MODE_8BIT_BYTE: 1 << 2, 587 | MODE_KANJI: 1 << 3 588 | }; 589 | 590 | //--------------------------------------------------------------------- 591 | // QRErrorCorrectLevel 592 | //--------------------------------------------------------------------- 593 | 594 | var QRErrorCorrectLevel = { 595 | L: 1, 596 | M: 0, 597 | Q: 3, 598 | H: 2 599 | }; 600 | 601 | //--------------------------------------------------------------------- 602 | // QRMaskPattern 603 | //--------------------------------------------------------------------- 604 | 605 | var QRMaskPattern = { 606 | PATTERN000: 0, 607 | PATTERN001: 1, 608 | PATTERN010: 2, 609 | PATTERN011: 3, 610 | PATTERN100: 4, 611 | PATTERN101: 5, 612 | PATTERN110: 6, 613 | PATTERN111: 7 614 | }; 615 | 616 | //--------------------------------------------------------------------- 617 | // QRUtil 618 | //--------------------------------------------------------------------- 619 | 620 | var QRUtil = function() { 621 | 622 | var PATTERN_POSITION_TABLE = [ 623 | [], 624 | [6, 18], 625 | [6, 22], 626 | [6, 26], 627 | [6, 30], 628 | [6, 34], 629 | [6, 22, 38], 630 | [6, 24, 42], 631 | [6, 26, 46], 632 | [6, 28, 50], 633 | [6, 30, 54], 634 | [6, 32, 58], 635 | [6, 34, 62], 636 | [6, 26, 46, 66], 637 | [6, 26, 48, 70], 638 | [6, 26, 50, 74], 639 | [6, 30, 54, 78], 640 | [6, 30, 56, 82], 641 | [6, 30, 58, 86], 642 | [6, 34, 62, 90], 643 | [6, 28, 50, 72, 94], 644 | [6, 26, 50, 74, 98], 645 | [6, 30, 54, 78, 102], 646 | [6, 28, 54, 80, 106], 647 | [6, 32, 58, 84, 110], 648 | [6, 30, 58, 86, 114], 649 | [6, 34, 62, 90, 118], 650 | [6, 26, 50, 74, 98, 122], 651 | [6, 30, 54, 78, 102, 126], 652 | [6, 26, 52, 78, 104, 130], 653 | [6, 30, 56, 82, 108, 134], 654 | [6, 34, 60, 86, 112, 138], 655 | [6, 30, 58, 86, 114, 142], 656 | [6, 34, 62, 90, 118, 146], 657 | [6, 30, 54, 78, 102, 126, 150], 658 | [6, 24, 50, 76, 102, 128, 154], 659 | [6, 28, 54, 80, 106, 132, 158], 660 | [6, 32, 58, 84, 110, 136, 162], 661 | [6, 26, 54, 82, 110, 138, 166], 662 | [6, 30, 58, 86, 114, 142, 170] 663 | ]; 664 | var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); 665 | var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); 666 | var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); 667 | 668 | var _this = {}; 669 | 670 | var getBCHDigit = function(data) { 671 | var digit = 0; 672 | while (data != 0) { 673 | digit += 1; 674 | data >>>= 1; 675 | } 676 | return digit; 677 | }; 678 | 679 | _this.getBCHTypeInfo = function(data) { 680 | var d = data << 10; 681 | while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { 682 | d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15))); 683 | } 684 | return ((data << 10) | d) ^ G15_MASK; 685 | }; 686 | 687 | _this.getBCHTypeNumber = function(data) { 688 | var d = data << 12; 689 | while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { 690 | d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18))); 691 | } 692 | return (data << 12) | d; 693 | }; 694 | 695 | _this.getPatternPosition = function(typeNumber) { 696 | return PATTERN_POSITION_TABLE[typeNumber - 1]; 697 | }; 698 | 699 | _this.getMaskFunction = function(maskPattern) { 700 | 701 | switch (maskPattern) { 702 | 703 | case QRMaskPattern.PATTERN000: 704 | return function(i, j) { 705 | return (i + j) % 2 == 0; }; 706 | case QRMaskPattern.PATTERN001: 707 | return function(i, j) { 708 | return i % 2 == 0; }; 709 | case QRMaskPattern.PATTERN010: 710 | return function(i, j) { 711 | return j % 3 == 0; }; 712 | case QRMaskPattern.PATTERN011: 713 | return function(i, j) { 714 | return (i + j) % 3 == 0; }; 715 | case QRMaskPattern.PATTERN100: 716 | return function(i, j) { 717 | return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0; }; 718 | case QRMaskPattern.PATTERN101: 719 | return function(i, j) { 720 | return (i * j) % 2 + (i * j) % 3 == 0; }; 721 | case QRMaskPattern.PATTERN110: 722 | return function(i, j) { 723 | return ((i * j) % 2 + (i * j) % 3) % 2 == 0; }; 724 | case QRMaskPattern.PATTERN111: 725 | return function(i, j) { 726 | return ((i * j) % 3 + (i + j) % 2) % 2 == 0; }; 727 | 728 | default: 729 | throw new Error('bad maskPattern:' + maskPattern); 730 | } 731 | }; 732 | 733 | _this.getErrorCorrectPolynomial = function(errorCorrectLength) { 734 | var a = qrPolynomial([1], 0); 735 | for (var i = 0; i < errorCorrectLength; i += 1) { 736 | a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0)); 737 | } 738 | return a; 739 | }; 740 | 741 | _this.getLengthInBits = function(mode, type) { 742 | 743 | if (1 <= type && type < 10) { 744 | 745 | // 1 - 9 746 | 747 | switch (mode) { 748 | case QRMode.MODE_NUMBER: 749 | return 10; 750 | case QRMode.MODE_ALPHA_NUM: 751 | return 9; 752 | case QRMode.MODE_8BIT_BYTE: 753 | return 8; 754 | case QRMode.MODE_KANJI: 755 | return 8; 756 | default: 757 | throw new Error('mode:' + mode); 758 | } 759 | 760 | } else if (type < 27) { 761 | 762 | // 10 - 26 763 | 764 | switch (mode) { 765 | case QRMode.MODE_NUMBER: 766 | return 12; 767 | case QRMode.MODE_ALPHA_NUM: 768 | return 11; 769 | case QRMode.MODE_8BIT_BYTE: 770 | return 16; 771 | case QRMode.MODE_KANJI: 772 | return 10; 773 | default: 774 | throw new Error('mode:' + mode); 775 | } 776 | 777 | } else if (type < 41) { 778 | 779 | // 27 - 40 780 | 781 | switch (mode) { 782 | case QRMode.MODE_NUMBER: 783 | return 14; 784 | case QRMode.MODE_ALPHA_NUM: 785 | return 13; 786 | case QRMode.MODE_8BIT_BYTE: 787 | return 16; 788 | case QRMode.MODE_KANJI: 789 | return 12; 790 | default: 791 | throw new Error('mode:' + mode); 792 | } 793 | 794 | } else { 795 | throw new Error('type:' + type); 796 | } 797 | }; 798 | 799 | _this.getLostPoint = function(qrcode) { 800 | 801 | var moduleCount = qrcode.getModuleCount(); 802 | 803 | var lostPoint = 0; 804 | 805 | // LEVEL1 806 | 807 | for (var row = 0; row < moduleCount; row += 1) { 808 | for (var col = 0; col < moduleCount; col += 1) { 809 | 810 | var sameCount = 0; 811 | var dark = qrcode.isDark(row, col); 812 | 813 | for (var r = -1; r <= 1; r += 1) { 814 | 815 | if (row + r < 0 || moduleCount <= row + r) { 816 | continue; 817 | } 818 | 819 | for (var c = -1; c <= 1; c += 1) { 820 | 821 | if (col + c < 0 || moduleCount <= col + c) { 822 | continue; 823 | } 824 | 825 | if (r == 0 && c == 0) { 826 | continue; 827 | } 828 | 829 | if (dark == qrcode.isDark(row + r, col + c)) { 830 | sameCount += 1; 831 | } 832 | } 833 | } 834 | 835 | if (sameCount > 5) { 836 | lostPoint += (3 + sameCount - 5); 837 | } 838 | } 839 | }; 840 | 841 | // LEVEL2 842 | 843 | for (var row = 0; row < moduleCount - 1; row += 1) { 844 | for (var col = 0; col < moduleCount - 1; col += 1) { 845 | var count = 0; 846 | if (qrcode.isDark(row, col)) count += 1; 847 | if (qrcode.isDark(row + 1, col)) count += 1; 848 | if (qrcode.isDark(row, col + 1)) count += 1; 849 | if (qrcode.isDark(row + 1, col + 1)) count += 1; 850 | if (count == 0 || count == 4) { 851 | lostPoint += 3; 852 | } 853 | } 854 | } 855 | 856 | // LEVEL3 857 | 858 | for (var row = 0; row < moduleCount; row += 1) { 859 | for (var col = 0; col < moduleCount - 6; col += 1) { 860 | if (qrcode.isDark(row, col) && !qrcode.isDark(row, col + 1) && qrcode.isDark(row, col + 2) && qrcode.isDark(row, col + 3) && qrcode.isDark(row, col + 4) && !qrcode.isDark(row, col + 5) && qrcode.isDark(row, col + 6)) { 861 | lostPoint += 40; 862 | } 863 | } 864 | } 865 | 866 | for (var col = 0; col < moduleCount; col += 1) { 867 | for (var row = 0; row < moduleCount - 6; row += 1) { 868 | if (qrcode.isDark(row, col) && !qrcode.isDark(row + 1, col) && qrcode.isDark(row + 2, col) && qrcode.isDark(row + 3, col) && qrcode.isDark(row + 4, col) && !qrcode.isDark(row + 5, col) && qrcode.isDark(row + 6, col)) { 869 | lostPoint += 40; 870 | } 871 | } 872 | } 873 | 874 | // LEVEL4 875 | 876 | var darkCount = 0; 877 | 878 | for (var col = 0; col < moduleCount; col += 1) { 879 | for (var row = 0; row < moduleCount; row += 1) { 880 | if (qrcode.isDark(row, col)) { 881 | darkCount += 1; 882 | } 883 | } 884 | } 885 | 886 | var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; 887 | lostPoint += ratio * 10; 888 | 889 | return lostPoint; 890 | }; 891 | 892 | return _this; 893 | }(); 894 | 895 | //--------------------------------------------------------------------- 896 | // QRMath 897 | //--------------------------------------------------------------------- 898 | 899 | var QRMath = function() { 900 | 901 | var EXP_TABLE = new Array(256); 902 | var LOG_TABLE = new Array(256); 903 | 904 | // initialize tables 905 | for (var i = 0; i < 8; i += 1) { 906 | EXP_TABLE[i] = 1 << i; 907 | } 908 | for (var i = 8; i < 256; i += 1) { 909 | EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8]; 910 | } 911 | for (var i = 0; i < 255; i += 1) { 912 | LOG_TABLE[EXP_TABLE[i]] = i; 913 | } 914 | 915 | var _this = {}; 916 | 917 | _this.glog = function(n) { 918 | 919 | if (n < 1) { 920 | throw new Error('glog(' + n + ')'); 921 | } 922 | 923 | return LOG_TABLE[n]; 924 | }; 925 | 926 | _this.gexp = function(n) { 927 | 928 | while (n < 0) { 929 | n += 255; 930 | } 931 | 932 | while (n >= 256) { 933 | n -= 255; 934 | } 935 | 936 | return EXP_TABLE[n]; 937 | }; 938 | 939 | return _this; 940 | }(); 941 | 942 | //--------------------------------------------------------------------- 943 | // qrPolynomial 944 | //--------------------------------------------------------------------- 945 | 946 | function qrPolynomial(num, shift) { 947 | 948 | if (typeof num.length == 'undefined') { 949 | throw new Error(num.length + '/' + shift); 950 | } 951 | 952 | var _num = function() { 953 | var offset = 0; 954 | while (offset < num.length && num[offset] == 0) { 955 | offset += 1; 956 | } 957 | var _num = new Array(num.length - offset + shift); 958 | for (var i = 0; i < num.length - offset; i += 1) { 959 | _num[i] = num[i + offset]; 960 | } 961 | return _num; 962 | }(); 963 | 964 | var _this = {}; 965 | 966 | _this.getAt = function(index) { 967 | return _num[index]; 968 | }; 969 | 970 | _this.getLength = function() { 971 | return _num.length; 972 | }; 973 | 974 | _this.multiply = function(e) { 975 | 976 | var num = new Array(_this.getLength() + e.getLength() - 1); 977 | 978 | for (var i = 0; i < _this.getLength(); i += 1) { 979 | for (var j = 0; j < e.getLength(); j += 1) { 980 | num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i)) + QRMath.glog(e.getAt(j))); 981 | } 982 | } 983 | 984 | return qrPolynomial(num, 0); 985 | }; 986 | 987 | _this.mod = function(e) { 988 | 989 | if (_this.getLength() - e.getLength() < 0) { 990 | return _this; 991 | } 992 | 993 | var ratio = QRMath.glog(_this.getAt(0)) - QRMath.glog(e.getAt(0)); 994 | 995 | var num = new Array(_this.getLength()); 996 | for (var i = 0; i < _this.getLength(); i += 1) { 997 | num[i] = _this.getAt(i); 998 | } 999 | 1000 | for (var i = 0; i < e.getLength(); i += 1) { 1001 | num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i)) + ratio); 1002 | } 1003 | 1004 | // recursive call 1005 | return qrPolynomial(num, 0).mod(e); 1006 | }; 1007 | 1008 | return _this; 1009 | }; 1010 | 1011 | //--------------------------------------------------------------------- 1012 | // QRRSBlock 1013 | //--------------------------------------------------------------------- 1014 | 1015 | var QRRSBlock = function() { 1016 | 1017 | var RS_BLOCK_TABLE = [ 1018 | 1019 | // L 1020 | // M 1021 | // Q 1022 | // H 1023 | 1024 | // 1 1025 | [1, 26, 19], 1026 | [1, 26, 16], 1027 | [1, 26, 13], 1028 | [1, 26, 9], 1029 | 1030 | // 2 1031 | [1, 44, 34], 1032 | [1, 44, 28], 1033 | [1, 44, 22], 1034 | [1, 44, 16], 1035 | 1036 | // 3 1037 | [1, 70, 55], 1038 | [1, 70, 44], 1039 | [2, 35, 17], 1040 | [2, 35, 13], 1041 | 1042 | // 4 1043 | [1, 100, 80], 1044 | [2, 50, 32], 1045 | [2, 50, 24], 1046 | [4, 25, 9], 1047 | 1048 | // 5 1049 | [1, 134, 108], 1050 | [2, 67, 43], 1051 | [2, 33, 15, 2, 34, 16], 1052 | [2, 33, 11, 2, 34, 12], 1053 | 1054 | // 6 1055 | [2, 86, 68], 1056 | [4, 43, 27], 1057 | [4, 43, 19], 1058 | [4, 43, 15], 1059 | 1060 | // 7 1061 | [2, 98, 78], 1062 | [4, 49, 31], 1063 | [2, 32, 14, 4, 33, 15], 1064 | [4, 39, 13, 1, 40, 14], 1065 | 1066 | // 8 1067 | [2, 121, 97], 1068 | [2, 60, 38, 2, 61, 39], 1069 | [4, 40, 18, 2, 41, 19], 1070 | [4, 40, 14, 2, 41, 15], 1071 | 1072 | // 9 1073 | [2, 146, 116], 1074 | [3, 58, 36, 2, 59, 37], 1075 | [4, 36, 16, 4, 37, 17], 1076 | [4, 36, 12, 4, 37, 13], 1077 | 1078 | // 10 1079 | [2, 86, 68, 2, 87, 69], 1080 | [4, 69, 43, 1, 70, 44], 1081 | [6, 43, 19, 2, 44, 20], 1082 | [6, 43, 15, 2, 44, 16], 1083 | 1084 | // 11 1085 | [4, 101, 81], 1086 | [1, 80, 50, 4, 81, 51], 1087 | [4, 50, 22, 4, 51, 23], 1088 | [3, 36, 12, 8, 37, 13], 1089 | 1090 | // 12 1091 | [2, 116, 92, 2, 117, 93], 1092 | [6, 58, 36, 2, 59, 37], 1093 | [4, 46, 20, 6, 47, 21], 1094 | [7, 42, 14, 4, 43, 15], 1095 | 1096 | // 13 1097 | [4, 133, 107], 1098 | [8, 59, 37, 1, 60, 38], 1099 | [8, 44, 20, 4, 45, 21], 1100 | [12, 33, 11, 4, 34, 12], 1101 | 1102 | // 14 1103 | [3, 145, 115, 1, 146, 116], 1104 | [4, 64, 40, 5, 65, 41], 1105 | [11, 36, 16, 5, 37, 17], 1106 | [11, 36, 12, 5, 37, 13], 1107 | 1108 | // 15 1109 | [5, 109, 87, 1, 110, 88], 1110 | [5, 65, 41, 5, 66, 42], 1111 | [5, 54, 24, 7, 55, 25], 1112 | [11, 36, 12, 7, 37, 13], 1113 | 1114 | // 16 1115 | [5, 122, 98, 1, 123, 99], 1116 | [7, 73, 45, 3, 74, 46], 1117 | [15, 43, 19, 2, 44, 20], 1118 | [3, 45, 15, 13, 46, 16], 1119 | 1120 | // 17 1121 | [1, 135, 107, 5, 136, 108], 1122 | [10, 74, 46, 1, 75, 47], 1123 | [1, 50, 22, 15, 51, 23], 1124 | [2, 42, 14, 17, 43, 15], 1125 | 1126 | // 18 1127 | [5, 150, 120, 1, 151, 121], 1128 | [9, 69, 43, 4, 70, 44], 1129 | [17, 50, 22, 1, 51, 23], 1130 | [2, 42, 14, 19, 43, 15], 1131 | 1132 | // 19 1133 | [3, 141, 113, 4, 142, 114], 1134 | [3, 70, 44, 11, 71, 45], 1135 | [17, 47, 21, 4, 48, 22], 1136 | [9, 39, 13, 16, 40, 14], 1137 | 1138 | // 20 1139 | [3, 135, 107, 5, 136, 108], 1140 | [3, 67, 41, 13, 68, 42], 1141 | [15, 54, 24, 5, 55, 25], 1142 | [15, 43, 15, 10, 44, 16], 1143 | 1144 | // 21 1145 | [4, 144, 116, 4, 145, 117], 1146 | [17, 68, 42], 1147 | [17, 50, 22, 6, 51, 23], 1148 | [19, 46, 16, 6, 47, 17], 1149 | 1150 | // 22 1151 | [2, 139, 111, 7, 140, 112], 1152 | [17, 74, 46], 1153 | [7, 54, 24, 16, 55, 25], 1154 | [34, 37, 13], 1155 | 1156 | // 23 1157 | [4, 151, 121, 5, 152, 122], 1158 | [4, 75, 47, 14, 76, 48], 1159 | [11, 54, 24, 14, 55, 25], 1160 | [16, 45, 15, 14, 46, 16], 1161 | 1162 | // 24 1163 | [6, 147, 117, 4, 148, 118], 1164 | [6, 73, 45, 14, 74, 46], 1165 | [11, 54, 24, 16, 55, 25], 1166 | [30, 46, 16, 2, 47, 17], 1167 | 1168 | // 25 1169 | [8, 132, 106, 4, 133, 107], 1170 | [8, 75, 47, 13, 76, 48], 1171 | [7, 54, 24, 22, 55, 25], 1172 | [22, 45, 15, 13, 46, 16], 1173 | 1174 | // 26 1175 | [10, 142, 114, 2, 143, 115], 1176 | [19, 74, 46, 4, 75, 47], 1177 | [28, 50, 22, 6, 51, 23], 1178 | [33, 46, 16, 4, 47, 17], 1179 | 1180 | // 27 1181 | [8, 152, 122, 4, 153, 123], 1182 | [22, 73, 45, 3, 74, 46], 1183 | [8, 53, 23, 26, 54, 24], 1184 | [12, 45, 15, 28, 46, 16], 1185 | 1186 | // 28 1187 | [3, 147, 117, 10, 148, 118], 1188 | [3, 73, 45, 23, 74, 46], 1189 | [4, 54, 24, 31, 55, 25], 1190 | [11, 45, 15, 31, 46, 16], 1191 | 1192 | // 29 1193 | [7, 146, 116, 7, 147, 117], 1194 | [21, 73, 45, 7, 74, 46], 1195 | [1, 53, 23, 37, 54, 24], 1196 | [19, 45, 15, 26, 46, 16], 1197 | 1198 | // 30 1199 | [5, 145, 115, 10, 146, 116], 1200 | [19, 75, 47, 10, 76, 48], 1201 | [15, 54, 24, 25, 55, 25], 1202 | [23, 45, 15, 25, 46, 16], 1203 | 1204 | // 31 1205 | [13, 145, 115, 3, 146, 116], 1206 | [2, 74, 46, 29, 75, 47], 1207 | [42, 54, 24, 1, 55, 25], 1208 | [23, 45, 15, 28, 46, 16], 1209 | 1210 | // 32 1211 | [17, 145, 115], 1212 | [10, 74, 46, 23, 75, 47], 1213 | [10, 54, 24, 35, 55, 25], 1214 | [19, 45, 15, 35, 46, 16], 1215 | 1216 | // 33 1217 | [17, 145, 115, 1, 146, 116], 1218 | [14, 74, 46, 21, 75, 47], 1219 | [29, 54, 24, 19, 55, 25], 1220 | [11, 45, 15, 46, 46, 16], 1221 | 1222 | // 34 1223 | [13, 145, 115, 6, 146, 116], 1224 | [14, 74, 46, 23, 75, 47], 1225 | [44, 54, 24, 7, 55, 25], 1226 | [59, 46, 16, 1, 47, 17], 1227 | 1228 | // 35 1229 | [12, 151, 121, 7, 152, 122], 1230 | [12, 75, 47, 26, 76, 48], 1231 | [39, 54, 24, 14, 55, 25], 1232 | [22, 45, 15, 41, 46, 16], 1233 | 1234 | // 36 1235 | [6, 151, 121, 14, 152, 122], 1236 | [6, 75, 47, 34, 76, 48], 1237 | [46, 54, 24, 10, 55, 25], 1238 | [2, 45, 15, 64, 46, 16], 1239 | 1240 | // 37 1241 | [17, 152, 122, 4, 153, 123], 1242 | [29, 74, 46, 14, 75, 47], 1243 | [49, 54, 24, 10, 55, 25], 1244 | [24, 45, 15, 46, 46, 16], 1245 | 1246 | // 38 1247 | [4, 152, 122, 18, 153, 123], 1248 | [13, 74, 46, 32, 75, 47], 1249 | [48, 54, 24, 14, 55, 25], 1250 | [42, 45, 15, 32, 46, 16], 1251 | 1252 | // 39 1253 | [20, 147, 117, 4, 148, 118], 1254 | [40, 75, 47, 7, 76, 48], 1255 | [43, 54, 24, 22, 55, 25], 1256 | [10, 45, 15, 67, 46, 16], 1257 | 1258 | // 40 1259 | [19, 148, 118, 6, 149, 119], 1260 | [18, 75, 47, 31, 76, 48], 1261 | [34, 54, 24, 34, 55, 25], 1262 | [20, 45, 15, 61, 46, 16] 1263 | ]; 1264 | 1265 | var qrRSBlock = function(totalCount, dataCount) { 1266 | var _this = {}; 1267 | _this.totalCount = totalCount; 1268 | _this.dataCount = dataCount; 1269 | return _this; 1270 | }; 1271 | 1272 | var _this = {}; 1273 | 1274 | var getRsBlockTable = function(typeNumber, errorCorrectLevel) { 1275 | 1276 | switch (errorCorrectLevel) { 1277 | case QRErrorCorrectLevel.L: 1278 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; 1279 | case QRErrorCorrectLevel.M: 1280 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; 1281 | case QRErrorCorrectLevel.Q: 1282 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; 1283 | case QRErrorCorrectLevel.H: 1284 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; 1285 | default: 1286 | return undefined; 1287 | } 1288 | }; 1289 | 1290 | _this.getRSBlocks = function(typeNumber, errorCorrectLevel) { 1291 | 1292 | var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel); 1293 | 1294 | if (typeof rsBlock == 'undefined') { 1295 | throw new Error('bad rs block @ typeNumber:' + typeNumber + 1296 | '/errorCorrectLevel:' + errorCorrectLevel); 1297 | } 1298 | 1299 | var length = rsBlock.length / 3; 1300 | 1301 | var list = new Array(); 1302 | 1303 | for (var i = 0; i < length; i += 1) { 1304 | 1305 | var count = rsBlock[i * 3 + 0]; 1306 | var totalCount = rsBlock[i * 3 + 1]; 1307 | var dataCount = rsBlock[i * 3 + 2]; 1308 | 1309 | for (var j = 0; j < count; j += 1) { 1310 | list.push(qrRSBlock(totalCount, dataCount)); 1311 | } 1312 | } 1313 | 1314 | return list; 1315 | }; 1316 | 1317 | return _this; 1318 | }(); 1319 | 1320 | //--------------------------------------------------------------------- 1321 | // qrBitBuffer 1322 | //--------------------------------------------------------------------- 1323 | 1324 | var qrBitBuffer = function() { 1325 | 1326 | var _buffer = new Array(); 1327 | var _length = 0; 1328 | 1329 | var _this = {}; 1330 | 1331 | _this.getBuffer = function() { 1332 | return _buffer; 1333 | }; 1334 | 1335 | _this.getAt = function(index) { 1336 | var bufIndex = Math.floor(index / 8); 1337 | return ((_buffer[bufIndex] >>> (7 - index % 8)) & 1) == 1; 1338 | }; 1339 | 1340 | _this.put = function(num, length) { 1341 | for (var i = 0; i < length; i += 1) { 1342 | _this.putBit(((num >>> (length - i - 1)) & 1) == 1); 1343 | } 1344 | }; 1345 | 1346 | _this.getLengthInBits = function() { 1347 | return _length; 1348 | }; 1349 | 1350 | _this.putBit = function(bit) { 1351 | 1352 | var bufIndex = Math.floor(_length / 8); 1353 | if (_buffer.length <= bufIndex) { 1354 | _buffer.push(0); 1355 | } 1356 | 1357 | if (bit) { 1358 | _buffer[bufIndex] |= (0x80 >>> (_length % 8)); 1359 | } 1360 | 1361 | _length += 1; 1362 | }; 1363 | 1364 | return _this; 1365 | }; 1366 | 1367 | //--------------------------------------------------------------------- 1368 | // qr8BitByte 1369 | //--------------------------------------------------------------------- 1370 | 1371 | var qr8BitByte = function(data) { 1372 | 1373 | var _mode = QRMode.MODE_8BIT_BYTE; 1374 | var _data = data; 1375 | var _bytes = qrcode.stringToBytes(data); 1376 | 1377 | var _this = {}; 1378 | 1379 | _this.getMode = function() { 1380 | return _mode; 1381 | }; 1382 | 1383 | _this.getLength = function(buffer) { 1384 | return _bytes.length; 1385 | }; 1386 | 1387 | _this.write = function(buffer) { 1388 | for (var i = 0; i < _bytes.length; i += 1) { 1389 | buffer.put(_bytes[i], 8); 1390 | } 1391 | }; 1392 | 1393 | return _this; 1394 | }; 1395 | 1396 | //===================================================================== 1397 | // GIF Support etc. 1398 | // 1399 | 1400 | //--------------------------------------------------------------------- 1401 | // byteArrayOutputStream 1402 | //--------------------------------------------------------------------- 1403 | 1404 | var byteArrayOutputStream = function() { 1405 | 1406 | var _bytes = new Array(); 1407 | 1408 | var _this = {}; 1409 | 1410 | _this.writeByte = function(b) { 1411 | _bytes.push(b & 0xff); 1412 | }; 1413 | 1414 | _this.writeShort = function(i) { 1415 | _this.writeByte(i); 1416 | _this.writeByte(i >>> 8); 1417 | }; 1418 | 1419 | _this.writeBytes = function(b, off, len) { 1420 | off = off || 0; 1421 | len = len || b.length; 1422 | for (var i = 0; i < len; i += 1) { 1423 | _this.writeByte(b[i + off]); 1424 | } 1425 | }; 1426 | 1427 | _this.writeString = function(s) { 1428 | for (var i = 0; i < s.length; i += 1) { 1429 | _this.writeByte(s.charCodeAt(i)); 1430 | } 1431 | }; 1432 | 1433 | _this.toByteArray = function() { 1434 | return _bytes; 1435 | }; 1436 | 1437 | _this.toString = function() { 1438 | var s = ''; 1439 | s += '['; 1440 | for (var i = 0; i < _bytes.length; i += 1) { 1441 | if (i > 0) { 1442 | s += ','; 1443 | } 1444 | s += _bytes[i]; 1445 | } 1446 | s += ']'; 1447 | return s; 1448 | }; 1449 | 1450 | return _this; 1451 | }; 1452 | 1453 | //--------------------------------------------------------------------- 1454 | // base64EncodeOutputStream 1455 | //--------------------------------------------------------------------- 1456 | 1457 | var base64EncodeOutputStream = function() { 1458 | 1459 | var _buffer = 0; 1460 | var _buflen = 0; 1461 | var _length = 0; 1462 | var _base64 = ''; 1463 | 1464 | var _this = {}; 1465 | 1466 | var writeEncoded = function(b) { 1467 | _base64 += String.fromCharCode(encode(b & 0x3f)); 1468 | }; 1469 | 1470 | var encode = function(n) { 1471 | if (n < 0) { 1472 | // error. 1473 | } else if (n < 26) { 1474 | return 0x41 + n; 1475 | } else if (n < 52) { 1476 | return 0x61 + (n - 26); 1477 | } else if (n < 62) { 1478 | return 0x30 + (n - 52); 1479 | } else if (n == 62) { 1480 | return 0x2b; 1481 | } else if (n == 63) { 1482 | return 0x2f; 1483 | } 1484 | throw new Error('n:' + n); 1485 | }; 1486 | 1487 | _this.writeByte = function(n) { 1488 | 1489 | _buffer = (_buffer << 8) | (n & 0xff); 1490 | _buflen += 8; 1491 | _length += 1; 1492 | 1493 | while (_buflen >= 6) { 1494 | writeEncoded(_buffer >>> (_buflen - 6)); 1495 | _buflen -= 6; 1496 | } 1497 | }; 1498 | 1499 | _this.flush = function() { 1500 | 1501 | if (_buflen > 0) { 1502 | writeEncoded(_buffer << (6 - _buflen)); 1503 | _buffer = 0; 1504 | _buflen = 0; 1505 | } 1506 | 1507 | if (_length % 3 != 0) { 1508 | // padding 1509 | var padlen = 3 - _length % 3; 1510 | for (var i = 0; i < padlen; i += 1) { 1511 | _base64 += '='; 1512 | } 1513 | } 1514 | }; 1515 | 1516 | _this.toString = function() { 1517 | return _base64; 1518 | }; 1519 | 1520 | return _this; 1521 | }; 1522 | 1523 | //--------------------------------------------------------------------- 1524 | // base64DecodeInputStream 1525 | //--------------------------------------------------------------------- 1526 | 1527 | var base64DecodeInputStream = function(str) { 1528 | 1529 | var _str = str; 1530 | var _pos = 0; 1531 | var _buffer = 0; 1532 | var _buflen = 0; 1533 | 1534 | var _this = {}; 1535 | 1536 | _this.read = function() { 1537 | 1538 | while (_buflen < 8) { 1539 | 1540 | if (_pos >= _str.length) { 1541 | if (_buflen == 0) { 1542 | return -1; 1543 | } 1544 | throw new Error('unexpected end of file./' + _buflen); 1545 | } 1546 | 1547 | var c = _str.charAt(_pos); 1548 | _pos += 1; 1549 | 1550 | if (c == '=') { 1551 | _buflen = 0; 1552 | return -1; 1553 | } else if (c.match(/^\s$/)) { 1554 | // ignore if whitespace. 1555 | continue; 1556 | } 1557 | 1558 | _buffer = (_buffer << 6) | decode(c.charCodeAt(0)); 1559 | _buflen += 6; 1560 | } 1561 | 1562 | var n = (_buffer >>> (_buflen - 8)) & 0xff; 1563 | _buflen -= 8; 1564 | return n; 1565 | }; 1566 | 1567 | var decode = function(c) { 1568 | if (0x41 <= c && c <= 0x5a) { 1569 | return c - 0x41; 1570 | } else if (0x61 <= c && c <= 0x7a) { 1571 | return c - 0x61 + 26; 1572 | } else if (0x30 <= c && c <= 0x39) { 1573 | return c - 0x30 + 52; 1574 | } else if (c == 0x2b) { 1575 | return 62; 1576 | } else if (c == 0x2f) { 1577 | return 63; 1578 | } else { 1579 | throw new Error('c:' + c); 1580 | } 1581 | }; 1582 | 1583 | return _this; 1584 | }; 1585 | 1586 | //--------------------------------------------------------------------- 1587 | // gifImage (B/W) 1588 | //--------------------------------------------------------------------- 1589 | 1590 | var gifImage = function(width, height) { 1591 | 1592 | var _width = width; 1593 | var _height = height; 1594 | var _data = new Array(width * height); 1595 | 1596 | var _this = {}; 1597 | 1598 | _this.setPixel = function(x, y, pixel) { 1599 | _data[y * _width + x] = pixel; 1600 | }; 1601 | 1602 | _this.write = function(out) { 1603 | 1604 | //--------------------------------- 1605 | // GIF Signature 1606 | 1607 | out.writeString('GIF87a'); 1608 | 1609 | //--------------------------------- 1610 | // Screen Descriptor 1611 | 1612 | out.writeShort(_width); 1613 | out.writeShort(_height); 1614 | 1615 | out.writeByte(0x80); // 2bit 1616 | out.writeByte(0); 1617 | out.writeByte(0); 1618 | 1619 | //--------------------------------- 1620 | // Global Color Map 1621 | 1622 | // black 1623 | out.writeByte(0x00); 1624 | out.writeByte(0x00); 1625 | out.writeByte(0x00); 1626 | 1627 | // white 1628 | out.writeByte(0xff); 1629 | out.writeByte(0xff); 1630 | out.writeByte(0xff); 1631 | 1632 | //--------------------------------- 1633 | // Image Descriptor 1634 | 1635 | out.writeString(','); 1636 | out.writeShort(0); 1637 | out.writeShort(0); 1638 | out.writeShort(_width); 1639 | out.writeShort(_height); 1640 | out.writeByte(0); 1641 | 1642 | //--------------------------------- 1643 | // Local Color Map 1644 | 1645 | //--------------------------------- 1646 | // Raster Data 1647 | 1648 | var lzwMinCodeSize = 2; 1649 | var raster = getLZWRaster(lzwMinCodeSize); 1650 | 1651 | out.writeByte(lzwMinCodeSize); 1652 | 1653 | var offset = 0; 1654 | 1655 | while (raster.length - offset > 255) { 1656 | out.writeByte(255); 1657 | out.writeBytes(raster, offset, 255); 1658 | offset += 255; 1659 | } 1660 | 1661 | out.writeByte(raster.length - offset); 1662 | out.writeBytes(raster, offset, raster.length - offset); 1663 | out.writeByte(0x00); 1664 | 1665 | //--------------------------------- 1666 | // GIF Terminator 1667 | out.writeString(';'); 1668 | }; 1669 | 1670 | var bitOutputStream = function(out) { 1671 | 1672 | var _out = out; 1673 | var _bitLength = 0; 1674 | var _bitBuffer = 0; 1675 | 1676 | var _this = {}; 1677 | 1678 | _this.write = function(data, length) { 1679 | 1680 | if ((data >>> length) != 0) { 1681 | throw new Error('length over'); 1682 | } 1683 | 1684 | while (_bitLength + length >= 8) { 1685 | _out.writeByte(0xff & ((data << _bitLength) | _bitBuffer)); 1686 | length -= (8 - _bitLength); 1687 | data >>>= (8 - _bitLength); 1688 | _bitBuffer = 0; 1689 | _bitLength = 0; 1690 | } 1691 | 1692 | _bitBuffer = (data << _bitLength) | _bitBuffer; 1693 | _bitLength = _bitLength + length; 1694 | }; 1695 | 1696 | _this.flush = function() { 1697 | if (_bitLength > 0) { 1698 | _out.writeByte(_bitBuffer); 1699 | } 1700 | }; 1701 | 1702 | return _this; 1703 | }; 1704 | 1705 | var getLZWRaster = function(lzwMinCodeSize) { 1706 | 1707 | var clearCode = 1 << lzwMinCodeSize; 1708 | var endCode = (1 << lzwMinCodeSize) + 1; 1709 | var bitLength = lzwMinCodeSize + 1; 1710 | 1711 | // Setup LZWTable 1712 | var table = lzwTable(); 1713 | 1714 | for (var i = 0; i < clearCode; i += 1) { 1715 | table.add(String.fromCharCode(i)); 1716 | } 1717 | table.add(String.fromCharCode(clearCode)); 1718 | table.add(String.fromCharCode(endCode)); 1719 | 1720 | var byteOut = byteArrayOutputStream(); 1721 | var bitOut = bitOutputStream(byteOut); 1722 | 1723 | // clear code 1724 | bitOut.write(clearCode, bitLength); 1725 | 1726 | var dataIndex = 0; 1727 | 1728 | var s = String.fromCharCode(_data[dataIndex]); 1729 | dataIndex += 1; 1730 | 1731 | while (dataIndex < _data.length) { 1732 | 1733 | var c = String.fromCharCode(_data[dataIndex]); 1734 | dataIndex += 1; 1735 | 1736 | if (table.contains(s + c)) { 1737 | 1738 | s = s + c; 1739 | 1740 | } else { 1741 | 1742 | bitOut.write(table.indexOf(s), bitLength); 1743 | 1744 | if (table.size() < 0xfff) { 1745 | 1746 | if (table.size() == (1 << bitLength)) { 1747 | bitLength += 1; 1748 | } 1749 | 1750 | table.add(s + c); 1751 | } 1752 | 1753 | s = c; 1754 | } 1755 | } 1756 | 1757 | bitOut.write(table.indexOf(s), bitLength); 1758 | 1759 | // end code 1760 | bitOut.write(endCode, bitLength); 1761 | 1762 | bitOut.flush(); 1763 | 1764 | return byteOut.toByteArray(); 1765 | }; 1766 | 1767 | var lzwTable = function() { 1768 | 1769 | var _map = {}; 1770 | var _size = 0; 1771 | 1772 | var _this = {}; 1773 | 1774 | _this.add = function(key) { 1775 | if (_this.contains(key)) { 1776 | throw new Error('dup key:' + key); 1777 | } 1778 | _map[key] = _size; 1779 | _size += 1; 1780 | }; 1781 | 1782 | _this.size = function() { 1783 | return _size; 1784 | }; 1785 | 1786 | _this.indexOf = function(key) { 1787 | return _map[key]; 1788 | }; 1789 | 1790 | _this.contains = function(key) { 1791 | return typeof _map[key] != 'undefined'; 1792 | }; 1793 | 1794 | return _this; 1795 | }; 1796 | 1797 | return _this; 1798 | }; 1799 | 1800 | var createImgTag = function(width, height, getPixel, alt) { 1801 | 1802 | var gif = gifImage(width, height); 1803 | for (var y = 0; y < height; y += 1) { 1804 | for (var x = 0; x < width; x += 1) { 1805 | gif.setPixel(x, y, getPixel(x, y)); 1806 | } 1807 | } 1808 | 1809 | var b = byteArrayOutputStream(); 1810 | gif.write(b); 1811 | 1812 | var base64 = base64EncodeOutputStream(); 1813 | var bytes = b.toByteArray(); 1814 | for (var i = 0; i < bytes.length; i += 1) { 1815 | base64.writeByte(bytes[i]); 1816 | } 1817 | base64.flush(); 1818 | 1819 | var img = ''; 1820 | img += ' 2 | 21 |
{{{issues}}}
22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gitbook-plugin-page-footer", 3 | "version": "5.5.0", 4 | "description": "a gitbook-plugin for generating footer", 5 | "engines": { 6 | "gitbook": ">=3.0.0-pre.0" 7 | }, 8 | "main": "./lib/index.js", 9 | "scripts": {}, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/aleen42/gitbook-footer.git" 13 | }, 14 | "keywords": [ 15 | "gitbook", 16 | "gitbook-plugin", 17 | "footer" 18 | ], 19 | "author": "aleen42 (http://aleen42.github.io/pc.html)", 20 | "contributors": [ 21 | "aleen42 ", 22 | "TuckerWhitehouse " 23 | ], 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/aleen42/gitbook-footer/issues" 27 | }, 28 | "homepage": "https://github.com/aleen42/gitbook-footer#readme", 29 | "dependencies": { 30 | "handlebars": "^4.7.7", 31 | "sync-request": "3.0.1", 32 | "node-cache": "3.2.1" 33 | }, 34 | "devDependencies": { 35 | "eslint": "^7.4.0", 36 | "eslint-config-aleen42": "^1.0.4" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /page-footer-symmetrical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aleen42/gitbook-footer/e8035d7fe0e28da99e4f62aa517f665f7f64dcb1/page-footer-symmetrical.png -------------------------------------------------------------------------------- /page-footer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aleen42/gitbook-footer/e8035d7fe0e28da99e4f62aa517f665f7f64dcb1/page-footer.png --------------------------------------------------------------------------------