├── .gitignore ├── src ├── demo │ ├── back.png │ ├── dummy.png │ ├── styles.css │ ├── scripts.js │ ├── index.html │ └── ff-range.js ├── jquery.qrcode.js └── qrcode.js ├── package.json ├── LICENSE.txt ├── README.md └── makefile.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | build 3 | local 4 | *.sublime-* 5 | -------------------------------------------------------------------------------- /src/demo/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaptive/jQuery.qrcode/develop/src/demo/back.png -------------------------------------------------------------------------------- /src/demo/dummy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adaptive/jQuery.qrcode/develop/src/demo/dummy.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.qrcode", 3 | "displayName": "jQuery.qrcode", 4 | "version": "0.6.0-dev" 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Lars Jung, http://larsjung.de 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery.qrcode 2 | 3 | * Website with download, docs and demo: 4 | * Sources: 5 | 6 | jQuery.qrcode is provided under the terms of the [MIT License](http://github.com/lrsjng/jQuery.qrcode/blob/master/LICENSE.txt). 7 | 8 | Uses [QR Code Generator](http://www.d-project.com/qrcode/index.html) (MIT). Kudos to [jquery.qrcode.js](http://github.com/jeromeetienne/jquery-qrcode) (MIT). 9 | 10 | 11 | ## Changelog 12 | 13 | 14 | ### develop branch 15 | 16 | * ... 17 | 18 | 19 | ### v0.6.0 - *2013-07-28* 20 | 21 | * adds version range 22 | * adds quiet zone 23 | * adds optional label or image 24 | * adds rounded corners 25 | * adds image output 26 | * adds demo 27 | 28 | 29 | ### v0.5.0 - *2013-07-23* 30 | 31 | * adds option to set error correction level 32 | 33 | 34 | ### v0.4 - *2013-07-09* 35 | 36 | * fixes missing default values in some cases 37 | 38 | 39 | ### v0.3 - *2013-06-01* 40 | 41 | * adds option to draw on existing canvas 42 | 43 | 44 | ### v0.2 - *2012-07-02* 45 | 46 | * now encodes up to ~2900 characters (8-bit) 47 | 48 | 49 | ### v0.1 - *2012-07-01* 50 | 51 | * Initial release 52 | -------------------------------------------------------------------------------- /src/demo/styles.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | font-family: Ubuntu; 4 | margin: 0; 5 | padding: 0; 6 | text-align: center; 7 | background: repeat url("back.png"); 8 | } 9 | 10 | #banner { 11 | display: block; 12 | text-decoration: none; 13 | color: rgb(29, 119, 194); 14 | padding: 12px 0 0; 15 | text-align: center; 16 | } 17 | 18 | #banner:hover { 19 | color: #333; 20 | } 21 | 22 | #container { 23 | display: inline-block; 24 | margin: 20px auto; 25 | box-shadow: 0 0 16px rgba(0,0,0,0.5); 26 | } 27 | 28 | #container > * { 29 | display: block; 30 | } 31 | 32 | .control { 33 | position: absolute; 34 | background-color: #f8f8f8; 35 | top: 0; 36 | width: 190px; 37 | box-shadow: 0 0 32px rgba(0,0,0,0.5); 38 | overflow: hidden; 39 | text-align: left; 40 | } 41 | 42 | .control.left { 43 | left: 0; 44 | } 45 | 46 | .control.right { 47 | right: 0; 48 | } 49 | 50 | hr { 51 | margin: 12px 0 0; 52 | padding: 0; 53 | border: none; 54 | height: 1px; 55 | background-color: rgba(0,0,0,0.1); 56 | } 57 | 58 | label { 59 | font-size: 10px; 60 | color: #bbb; 61 | padding: 12px 4px 0 4px; 62 | } 63 | 64 | html.opera label[for="radius"] { 65 | color: #e55; 66 | } 67 | 68 | #download { 69 | text-align: center; 70 | font-weight: bold; 71 | text-decoration: none; 72 | display: block; 73 | color: #555; 74 | background-color: #ddd; 75 | margin: 4px; 76 | padding: 8px 0; 77 | border: 1px solid #ddd; 78 | cursor: pointer; 79 | } 80 | 81 | input, textarea, select { 82 | font-family: Ubuntu; 83 | display: block; 84 | background-color: #fff; 85 | margin: 2px; 86 | padding: 0 2px; 87 | border: 1px solid #ddd; 88 | width: 180px; 89 | height: 22px; 90 | } 91 | 92 | #text { 93 | height: 48px; 94 | } 95 | 96 | #img-buffer { 97 | display: none; 98 | } 99 | 100 | 101 | 102 | input[type="range"] { 103 | -webkit-appearance: none; 104 | /* height: 8px; 105 | margin-top: 4px; 106 | margin-bottom: 4px; 107 | */ cursor: pointer; 108 | } 109 | input::-webkit-slider-thumb { 110 | -webkit-appearance: none; 111 | width: 16px; 112 | height: 16px; 113 | border-radius: 3px; 114 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #aaa), color-stop(1, #aaa) ); 115 | } 116 | -------------------------------------------------------------------------------- /src/demo/scripts.js: -------------------------------------------------------------------------------- 1 | 2 | var isOpera = Object.prototype.toString.call(window.opera) == '[object Opera]', 3 | 4 | guiValuePairs = [ 5 | ["size", "px"], 6 | ["minversion", ""], 7 | ["quiet", "modules"], 8 | ["radius", "%"], 9 | ["msize", "%"], 10 | ["mposx", "%"], 11 | ["mposy", "%"] 12 | ], 13 | 14 | updateGui = function () { 15 | 16 | for (var idx in guiValuePairs) { 17 | 18 | var pair = guiValuePairs[idx], 19 | $label = $('label[for="' + pair[0] + '"]'); 20 | 21 | $label.text($label.text().replace(/:.*/, ': ' + $('#' + pair[0]).val() + pair[1])); 22 | } 23 | }, 24 | 25 | updateQrCode = function () { 26 | 27 | var options = { 28 | render: $("#render").val(), 29 | ecLevel: $("#eclevel").val(), 30 | minVersion: parseInt($("#minversion").val(), 10), 31 | 32 | fill: $("#fill").val(), 33 | background: $("#background").val(), 34 | // fill: $("#img-buffer")[0], 35 | 36 | text: $("#text").val(), 37 | size: parseInt($("#size").val(), 10), 38 | radius: parseInt($("#radius").val(), 10) * 0.01, 39 | quiet: parseInt($("#quiet").val(), 10), 40 | 41 | mode: parseInt($("#mode").val(), 10), 42 | 43 | mSize: parseInt($("#msize").val(), 10) * 0.01, 44 | mPosX: parseInt($("#mposx").val(), 10) * 0.01, 45 | mPosY: parseInt($("#mposy").val(), 10) * 0.01, 46 | 47 | label: $("#label").val(), 48 | fontname: $("#font").val(), 49 | fontcolor: $("#fontcolor").val(), 50 | 51 | image: $("#img-buffer")[0] 52 | }; 53 | 54 | $("#container").empty().qrcode(options); 55 | }, 56 | 57 | update = function () { 58 | 59 | updateGui(); 60 | updateQrCode(); 61 | }, 62 | 63 | onImageInput = function () { 64 | 65 | var input = $("#image")[0]; 66 | 67 | if (input.files && input.files[0]) { 68 | 69 | var reader = new FileReader(); 70 | 71 | reader.onload = function (event) { 72 | $("#img-buffer").attr("src", event.target.result); 73 | $("#mode").val("4"); 74 | setTimeout(update, 250); 75 | }; 76 | reader.readAsDataURL(input.files[0]); 77 | } 78 | }, 79 | 80 | download = function (event) { 81 | 82 | var data = $("#container canvas")[0].toDataURL('image/png'); 83 | $("#download").attr("href", data); 84 | }; 85 | 86 | 87 | $(function () { 88 | 89 | if (isOpera) { 90 | $('html').addClass('opera'); 91 | $('#radius').prop('disabled', true); 92 | } 93 | 94 | $("#download").on("click", download); 95 | $("#image").on('change', onImageInput); 96 | $("input, textarea, select").on("input change", update); 97 | $(window).load(update); 98 | update(); 99 | }); 100 | -------------------------------------------------------------------------------- /src/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{pkg.displayName}} {{pkg.version}} · Demo 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 | 30 | 35 |
36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 | 62 |
63 | 64 | 71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 |
79 | 80 | 81 | 82 | 83 | 84 | 85 |
86 | 87 | 88 |
89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /makefile.js: -------------------------------------------------------------------------------- 1 | /*jshint node: true */ 2 | 'use strict'; 3 | 4 | 5 | var path = require('path'), 6 | child_process = require('child_process'); 7 | 8 | 9 | var pkg = require('./package.json'), 10 | 11 | root = path.resolve(__dirname), 12 | src = path.resolve(root, 'src'), 13 | build = path.resolve(root, 'build'), 14 | 15 | jshint = { 16 | // Enforcing Options 17 | bitwise: true, 18 | curly: true, 19 | eqeqeq: true, 20 | forin: true, 21 | latedef: true, 22 | newcap: true, 23 | noempty: true, 24 | plusplus: true, 25 | trailing: true, 26 | undef: true, 27 | 28 | // Environments 29 | browser: true, 30 | 31 | // Globals 32 | predef: [ 33 | "jQuery", "qrcode" 34 | ] 35 | }, 36 | 37 | mapperSrc = function (blob) { 38 | 39 | return blob.source.replace(src, build); 40 | }, 41 | 42 | mapperRoot = function (blob) { 43 | 44 | return blob.source.replace(root, build); 45 | }; 46 | 47 | 48 | module.exports = function (make) { 49 | 50 | var Event = make.Event, 51 | $ = make.fQuery, 52 | moment = make.moment, 53 | stamp, replacements; 54 | 55 | 56 | make.version('>=0.8.1'); 57 | make.defaults('release'); 58 | 59 | 60 | make.before(function () { 61 | 62 | stamp = moment(); 63 | 64 | replacements = { 65 | pkg: pkg, 66 | stamp: stamp.format('YYYY-MM-DD HH:mm:ss') 67 | }; 68 | 69 | Event.info({ method: 'before', message: pkg.version + ' ' + replacements.stamp }); 70 | }); 71 | 72 | 73 | make.target('check-version', [], 'add git info to dev builds').async(function (done, fail) { 74 | 75 | if (!/-dev$/.test(pkg.version)) { 76 | done(); 77 | return; 78 | } 79 | 80 | $.git(root, function (err, result) { 81 | 82 | pkg.version += '-' + result.revListOriginMasterHead.length + '-' + result.revParseHead.slice(0, 7); 83 | Event.info({ 84 | method: 'check-version', 85 | message: 'version set to ' + pkg.version 86 | }); 87 | done(); 88 | }); 89 | }); 90 | 91 | 92 | make.target('clean', [], 'delete build folder') 93 | .sync(function () { 94 | 95 | $.rmfr($.I_AM_SURE, build); 96 | }); 97 | 98 | 99 | make.target('lint', [], 'lint all JavaScript files with JSHint') 100 | .sync(function () { 101 | 102 | $(src + ': jquery.qrcode.js') 103 | .jshint(jshint); 104 | }); 105 | 106 | 107 | make.target('build', ['check-version'], 'build all updated files') 108 | .sync(function () { 109 | 110 | var scriptName = pkg.name; 111 | 112 | $(src + '/demo/*') 113 | .handlebars(replacements) 114 | .write($.OVERWRITE, mapperSrc); 115 | 116 | $(src + ': ' + scriptName + '.js') 117 | .includify() 118 | .handlebars(replacements) 119 | .write($.OVERWRITE, path.join(build, scriptName + '-' + pkg.version + '.js')) 120 | .write($.OVERWRITE, path.join(build, 'demo', scriptName + '.js')) 121 | .uglifyjs() 122 | .write($.OVERWRITE, path.join(build, scriptName + '-' + pkg.version + '.min.js')); 123 | 124 | $(root + ': README*, LICENSE*') 125 | .write($.OVERWRITE, mapperRoot); 126 | }); 127 | 128 | 129 | make.target('release', ['clean', 'build'], 'create a zipball') 130 | .async(function (done, fail) { 131 | 132 | var target = path.join(build, pkg.name + '-' + pkg.version + '.zip'), 133 | cmd = 'zip', 134 | args = ['-ro', target, '.'], 135 | options = { cwd: build }, 136 | proc = child_process.spawn(cmd, args, options); 137 | 138 | Event.info({ method: 'exec', message: cmd + ' ' + args.join(' ') }); 139 | 140 | proc.stderr.on('data', function (data) { 141 | process.stderr.write(data); 142 | }); 143 | proc.on('exit', function (code) { 144 | if (code) { 145 | Event.error({ method: 'exec', message: cmd + ' exit code ' + code }); 146 | fail(); 147 | } else { 148 | Event.ok({ method: 'exec', message: 'created zipball ' + target }); 149 | done(); 150 | } 151 | }); 152 | }); 153 | }; 154 | -------------------------------------------------------------------------------- /src/demo/ff-range.js: -------------------------------------------------------------------------------- 1 | /* 2 | html5slider - a JS implementation of for Firefox 16 and up 3 | https://github.com/fryn/html5slider 4 | 5 | Copyright (c) 2010-2013 Frank Yan, 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | */ 25 | 26 | (function() { 27 | 28 | // test for native support 29 | var test = document.createElement('input'); 30 | try { 31 | test.type = 'range'; 32 | if (test.type == 'range') 33 | return; 34 | } catch (e) { 35 | return; 36 | } 37 | 38 | // test for required property support 39 | test.style.background = 'linear-gradient(red, red)'; 40 | if (!test.style.backgroundImage || !('MozAppearance' in test.style)) 41 | return; 42 | 43 | var scale; 44 | var isMac = navigator.platform == 'MacIntel'; 45 | var thumb = { 46 | radius: isMac ? 9 : 6, 47 | width: isMac ? 22 : 12, 48 | height: isMac ? 16 : 20 49 | }; 50 | var track = 'linear-gradient(transparent ' + (isMac ? 51 | '6px, #999 6px, #999 7px, #ccc 8px, #bbb 9px, #bbb 10px, transparent 10px' : 52 | '9px, #999 9px, #bbb 10px, #fff 11px, transparent 11px') + 53 | ', transparent)'; 54 | var styles = { 55 | 'min-width': thumb.width + 'px', 56 | 'min-height': thumb.height + 'px', 57 | 'max-height': thumb.height + 'px', 58 | padding: '0 0 ' + (isMac ? '2px' : '1px'), 59 | border: 0, 60 | 'border-radius': 0, 61 | cursor: 'default', 62 | 'text-indent': '-999999px' // -moz-user-select: none; breaks mouse capture 63 | }; 64 | var options = { 65 | attributes: true, 66 | attributeFilter: ['min', 'max', 'step', 'value'] 67 | }; 68 | var onInput = document.createEvent('HTMLEvents'); 69 | onInput.initEvent('input', true, false); 70 | var onChange = document.createEvent('HTMLEvents'); 71 | onChange.initEvent('change', true, false); 72 | 73 | if (document.readyState == 'loading') 74 | document.addEventListener('DOMContentLoaded', initialize, true); 75 | else 76 | initialize(); 77 | addEventListener('pageshow', recreate, true); 78 | 79 | function initialize() { 80 | // create initial sliders 81 | recreate(); 82 | // create sliders on-the-fly 83 | new MutationObserver(function(mutations) { 84 | mutations.forEach(function(mutation) { 85 | if (mutation.addedNodes) 86 | Array.forEach(mutation.addedNodes, function(node) { 87 | if (!(node instanceof Element)) 88 | ; 89 | else if (node.childElementCount) 90 | Array.forEach(node.querySelectorAll('input[type=range]'), check); 91 | else if (node.mozMatchesSelector('input[type=range]')) 92 | check(node); 93 | }); 94 | }); 95 | }).observe(document, { childList: true, subtree: true }); 96 | } 97 | 98 | function recreate() { 99 | Array.forEach(document.querySelectorAll('input[type=range]'), check); 100 | } 101 | 102 | function check(input) { 103 | if (input.type != 'range') 104 | transform(input); 105 | } 106 | 107 | function transform(slider) { 108 | 109 | var isValueSet, areAttrsSet, isUI, isClick, prevValue, rawValue, prevX; 110 | var min, max, step, range, value = slider.value; 111 | 112 | // lazily create shared slider affordance 113 | if (!scale) { 114 | scale = document.body.appendChild(document.createElement('hr')); 115 | style(scale, { 116 | '-moz-appearance': isMac ? 'scale-horizontal' : 'scalethumb-horizontal', 117 | display: 'block', 118 | visibility: 'visible', 119 | opacity: 1, 120 | position: 'fixed', 121 | top: '-999999px' 122 | }); 123 | document.mozSetImageElement('__sliderthumb__', scale); 124 | } 125 | 126 | // reimplement value and type properties 127 | var getValue = function() { return '' + value; }; 128 | var setValue = function setValue(val) { 129 | value = '' + val; 130 | isValueSet = true; 131 | draw(); 132 | delete slider.value; 133 | slider.value = value; 134 | slider.__defineGetter__('value', getValue); 135 | slider.__defineSetter__('value', setValue); 136 | }; 137 | slider.__defineGetter__('value', getValue); 138 | slider.__defineSetter__('value', setValue); 139 | Object.defineProperty(slider, 'type', { 140 | get: function() { return 'range'; } 141 | }); 142 | 143 | // sync properties with attributes 144 | ['min', 'max', 'step'].forEach(function(name) { 145 | if (slider.hasAttribute(name)) 146 | areAttrsSet = true; 147 | Object.defineProperty(slider, name, { 148 | get: function() { 149 | return this.hasAttribute(name) ? this.getAttribute(name) : ''; 150 | }, 151 | set: function(val) { 152 | val === null ? 153 | this.removeAttribute(name) : 154 | this.setAttribute(name, val); 155 | } 156 | }); 157 | }); 158 | 159 | // initialize slider 160 | slider.readOnly = true; 161 | style(slider, styles); 162 | update(); 163 | 164 | new MutationObserver(function(mutations) { 165 | mutations.forEach(function(mutation) { 166 | if (mutation.attributeName != 'value') { 167 | update(); 168 | areAttrsSet = true; 169 | } 170 | // note that value attribute only sets initial value 171 | else if (!isValueSet) { 172 | value = slider.getAttribute('value'); 173 | draw(); 174 | } 175 | }); 176 | }).observe(slider, options); 177 | 178 | slider.addEventListener('mousedown', onDragStart, true); 179 | slider.addEventListener('keydown', onKeyDown, true); 180 | slider.addEventListener('focus', onFocus, true); 181 | slider.addEventListener('blur', onBlur, true); 182 | 183 | function onDragStart(e) { 184 | isClick = true; 185 | setTimeout(function() { isClick = false; }, 0); 186 | if (e.button || !range) 187 | return; 188 | var width = parseFloat(getComputedStyle(this).width); 189 | var multiplier = (width - thumb.width) / range; 190 | if (!multiplier) 191 | return; 192 | // distance between click and center of thumb 193 | var dev = e.clientX - this.getBoundingClientRect().left - thumb.width / 2 - 194 | (value - min) * multiplier; 195 | // if click was not on thumb, move thumb to click location 196 | if (Math.abs(dev) > thumb.radius) { 197 | isUI = true; 198 | this.value -= -dev / multiplier; 199 | } 200 | rawValue = value; 201 | prevX = e.clientX; 202 | this.addEventListener('mousemove', onDrag, true); 203 | this.addEventListener('mouseup', onDragEnd, true); 204 | } 205 | 206 | function onDrag(e) { 207 | var width = parseFloat(getComputedStyle(this).width); 208 | var multiplier = (width - thumb.width) / range; 209 | if (!multiplier) 210 | return; 211 | rawValue += (e.clientX - prevX) / multiplier; 212 | prevX = e.clientX; 213 | isUI = true; 214 | this.value = rawValue; 215 | } 216 | 217 | function onDragEnd() { 218 | this.removeEventListener('mousemove', onDrag, true); 219 | this.removeEventListener('mouseup', onDragEnd, true); 220 | slider.dispatchEvent(onInput); 221 | slider.dispatchEvent(onChange); 222 | } 223 | 224 | function onKeyDown(e) { 225 | if (e.keyCode > 36 && e.keyCode < 41) { // 37-40: left, up, right, down 226 | onFocus.call(this); 227 | isUI = true; 228 | this.value = value + (e.keyCode == 38 || e.keyCode == 39 ? step : -step); 229 | } 230 | } 231 | 232 | function onFocus() { 233 | if (!isClick) 234 | this.style.boxShadow = !isMac ? '0 0 0 2px #fb0' : 235 | 'inset 0 0 20px rgba(0,127,255,.1), 0 0 1px rgba(0,127,255,.4)'; 236 | } 237 | 238 | function onBlur() { 239 | this.style.boxShadow = ''; 240 | } 241 | 242 | // determines whether value is valid number in attribute form 243 | function isAttrNum(value) { 244 | return !isNaN(value) && +value == parseFloat(value); 245 | } 246 | 247 | // validates min, max, and step attributes and redraws 248 | function update() { 249 | min = isAttrNum(slider.min) ? +slider.min : 0; 250 | max = isAttrNum(slider.max) ? +slider.max : 100; 251 | if (max < min) 252 | max = min > 100 ? min : 100; 253 | step = isAttrNum(slider.step) && slider.step > 0 ? +slider.step : 1; 254 | range = max - min; 255 | draw(true); 256 | } 257 | 258 | // recalculates value property 259 | function calc() { 260 | if (!isValueSet && !areAttrsSet) 261 | value = slider.getAttribute('value'); 262 | if (!isAttrNum(value)) 263 | value = (min + max) / 2;; 264 | // snap to step intervals (WebKit sometimes does not - bug?) 265 | value = Math.round((value - min) / step) * step + min; 266 | if (value < min) 267 | value = min; 268 | else if (value > max) 269 | value = min + ~~(range / step) * step; 270 | } 271 | 272 | // renders slider using CSS background ;) 273 | function draw(attrsModified) { 274 | calc(); 275 | var wasUI = isUI; 276 | isUI = false; 277 | if (wasUI && value != prevValue) 278 | slider.dispatchEvent(onInput); 279 | if (!attrsModified && value == prevValue) 280 | return; 281 | prevValue = value; 282 | var position = range ? (value - min) / range * 100 : 0; 283 | var bg = '-moz-element(#__sliderthumb__) ' + position + '% no-repeat, '; 284 | style(slider, { background: bg + track }); 285 | } 286 | 287 | } 288 | 289 | function style(element, styles) { 290 | for (var prop in styles) 291 | element.style.setProperty(prop, styles[prop], 'important'); 292 | } 293 | 294 | })(); 295 | -------------------------------------------------------------------------------- /src/jquery.qrcode.js: -------------------------------------------------------------------------------- 1 | /*! {{pkg.displayName}} {{pkg.version}} - //larsjung.de/qrcode - MIT License */ 2 | 3 | // Uses [QR Code Generator](http://www.d-project.com/qrcode/index.html) (MIT), appended to the end of this file. 4 | // Kudos to [jquery.qrcode.js](http://github.com/jeromeetienne/jquery-qrcode) (MIT). 5 | 6 | (function ($) { 7 | 'use strict'; 8 | 9 | 10 | // Wrapper for the original QR code generator. 11 | var QRCode = function (text, level, version, quiet) { 12 | 13 | // `qrcode` is the single public function that will be defined by the `QR Code Generator` 14 | // at the end of the file. 15 | var qr = qrcode(version, level); 16 | qr.addData(text); 17 | qr.make(); 18 | 19 | quiet = quiet || 0; 20 | 21 | var qrModuleCount = qr.getModuleCount(), 22 | quietModuleCount = qr.getModuleCount() + 2*quiet, 23 | isDark = function (row, col) { 24 | 25 | row -= quiet; 26 | col -= quiet; 27 | 28 | if (row < 0 || row >= qrModuleCount || col < 0 || col >= qrModuleCount) { 29 | return false; 30 | } 31 | 32 | return qr.isDark(row, col); 33 | }, 34 | addBlank = function (l, t, r, b) { 35 | 36 | var prevIsDark = this.isDark, 37 | moduleSize = 1 / quietModuleCount; 38 | 39 | this.isDark = function (row, col) { 40 | 41 | var ml = col * moduleSize, 42 | mt = row * moduleSize, 43 | mr = ml + moduleSize, 44 | mb = mt + moduleSize; 45 | 46 | return prevIsDark(row, col) && (l > mr || ml > r || t > mb || mt > b); 47 | }; 48 | }; 49 | 50 | this.text = text; 51 | this.level = level; 52 | this.version = version; 53 | this.moduleCount = quietModuleCount; 54 | this.isDark = isDark; 55 | this.addBlank = addBlank; 56 | }, 57 | 58 | // Check if canvas is available in the browser (as Modernizr does) 59 | canvasAvailable = (function () { 60 | 61 | var elem = document.createElement('canvas'); 62 | return !!(elem.getContext && elem.getContext('2d')); 63 | }()), 64 | 65 | arcToAvailable = Object.prototype.toString.call(window.opera) !== '[object Opera]', 66 | 67 | // Returns a minimal QR code for the given text starting with version `minVersion`. 68 | // Returns `null` if `text` is too long to be encoded in `maxVersion`. 69 | createQRCode = function (text, level, minVersion, maxVersion, quiet) { 70 | 71 | minVersion = Math.max(1, minVersion || 1); 72 | maxVersion = Math.min(40, maxVersion || 40); 73 | for (var version = minVersion; version <= maxVersion; version += 1) { 74 | try { 75 | return new QRCode(text, level, version, quiet); 76 | } catch (err) {} 77 | } 78 | }, 79 | 80 | drawBackgroundLabel = function (qr, context, settings) { 81 | 82 | var size = settings.size, 83 | font = "bold " + (settings.mSize * size) + "px " + settings.fontname, 84 | ctx = $('')[0].getContext("2d"); 85 | 86 | ctx.font = font; 87 | 88 | var w = ctx.measureText(settings.label).width, 89 | sh = settings.mSize, 90 | sw = w / size, 91 | sl = (1 - sw) * settings.mPosX, 92 | st = (1 - sh) * settings.mPosY, 93 | sr = sl + sw, 94 | sb = st + sh, 95 | pad = 0.01; 96 | 97 | if (settings.mode === 1) { 98 | // Strip 99 | qr.addBlank(0, st - pad, size, sb + pad); 100 | } else { 101 | // Box 102 | qr.addBlank(sl - pad, st - pad, sr + pad, sb + pad); 103 | } 104 | 105 | context.fillStyle = settings.fontcolor; 106 | context.font = font; 107 | context.fillText($("#label").val(), sl*size, st*size + 0.75 * settings.mSize * size); 108 | }, 109 | 110 | drawBackgroundImage = function (qr, context, settings) { 111 | 112 | var size = settings.size, 113 | w = settings.image.naturalWidth || 1, 114 | h = settings.image.naturalHeight || 1, 115 | sh = settings.mSize, 116 | sw = sh * w / h, 117 | sl = (1 - sw) * settings.mPosX, 118 | st = (1 - sh) * settings.mPosY, 119 | sr = sl + sw, 120 | sb = st + sh, 121 | pad = 0.01; 122 | 123 | if (settings.mode === 3) { 124 | // Strip 125 | qr.addBlank(0, st - pad, size, sb + pad); 126 | } else { 127 | // Box 128 | qr.addBlank(sl - pad, st - pad, sr + pad, sb + pad); 129 | } 130 | 131 | context.drawImage(settings.image, sl*size, st*size, sw*size, sh*size); 132 | }, 133 | 134 | drawBackground = function (qr, context, settings) { 135 | 136 | if ($(settings.background).is('img')) { 137 | context.drawImage(settings.background, 0, 0, settings.size, settings.size); 138 | } else if (settings.background) { 139 | context.fillStyle = settings.background; 140 | context.fillRect(settings.left, settings.top, settings.size, settings.size); 141 | } 142 | 143 | var mode = settings.mode; 144 | if (mode === 1 || mode === 2) { 145 | drawBackgroundLabel(qr, context, settings); 146 | } else if (mode === 3 || mode === 4) { 147 | drawBackgroundImage(qr, context, settings); 148 | } 149 | }, 150 | 151 | drawModuleDefault = function (qr, context, settings, left, top, width, row, col) { 152 | 153 | if (qr.isDark(row, col)) { 154 | context.rect(left, top, width, width); 155 | } 156 | }, 157 | 158 | drawModuleRoundedDark = function (ctx, l, t, r, b, rad, nw, ne, se, sw) { 159 | 160 | if (nw) { 161 | ctx.moveTo(l + rad, t); 162 | } else { 163 | ctx.moveTo(l, t); 164 | } 165 | 166 | if (ne) { 167 | ctx.lineTo(r - rad, t); 168 | ctx.arcTo(r, t, r, b, rad); 169 | } else { 170 | ctx.lineTo(r, t); 171 | } 172 | 173 | if (se) { 174 | ctx.lineTo(r, b - rad); 175 | ctx.arcTo(r, b, l, b, rad); 176 | } else { 177 | ctx.lineTo(r, b); 178 | } 179 | 180 | if (sw) { 181 | ctx.lineTo(l + rad, b); 182 | ctx.arcTo(l, b, l, t, rad); 183 | } else { 184 | ctx.lineTo(l, b); 185 | } 186 | 187 | if (nw) { 188 | ctx.lineTo(l, t + rad); 189 | ctx.arcTo(l, t, r, t, rad); 190 | } else { 191 | ctx.lineTo(l, t); 192 | } 193 | }, 194 | 195 | drawModuleRoundendLight = function (ctx, l, t, r, b, rad, nw, ne, se, sw) { 196 | 197 | if (nw) { 198 | ctx.moveTo(l + rad, t); 199 | ctx.lineTo(l, t); 200 | ctx.lineTo(l, t + rad); 201 | ctx.arcTo(l, t, l + rad, t, rad); 202 | } 203 | 204 | if (ne) { 205 | ctx.moveTo(r - rad, t); 206 | ctx.lineTo(r, t); 207 | ctx.lineTo(r, t + rad); 208 | ctx.arcTo(r, t, r - rad, t, rad); 209 | } 210 | 211 | if (se) { 212 | ctx.moveTo(r - rad, b); 213 | ctx.lineTo(r, b); 214 | ctx.lineTo(r, b - rad); 215 | ctx.arcTo(r, b, r - rad, b, rad); 216 | } 217 | 218 | if (sw) { 219 | ctx.moveTo(l + rad, b); 220 | ctx.lineTo(l, b); 221 | ctx.lineTo(l, b - rad); 222 | ctx.arcTo(l, b, l + rad, b, rad); 223 | } 224 | }, 225 | 226 | drawModuleRounded = function (qr, context, settings, left, top, width, row, col) { 227 | 228 | var isDark = qr.isDark, 229 | right = left + width, 230 | bottom = top + width, 231 | radius = settings.radius * width, 232 | rowT = row - 1, 233 | rowB = row + 1, 234 | colL = col - 1, 235 | colR = col + 1, 236 | center = isDark(row, col), 237 | northwest = isDark(rowT, colL), 238 | north = isDark(rowT, col), 239 | northeast = isDark(rowT, colR), 240 | east = isDark(row, colR), 241 | southeast = isDark(rowB, colR), 242 | south = isDark(rowB, col), 243 | southwest = isDark(rowB, colL), 244 | west = isDark(row, colL); 245 | 246 | if (center) { 247 | drawModuleRoundedDark(context, left, top, right, bottom, radius, !north && !west, !north && !east, !south && !east, !south && !west); 248 | } else { 249 | drawModuleRoundendLight(context, left, top, right, bottom, radius, north && west && northwest, north && east && northeast, south && east && southeast, south && west && southwest); 250 | } 251 | }, 252 | 253 | drawModules = function (qr, context, settings) { 254 | 255 | var moduleCount = qr.moduleCount, 256 | moduleSize = settings.size / moduleCount, 257 | fn = drawModuleDefault, 258 | row, col; 259 | 260 | if (arcToAvailable && settings.radius > 0 && settings.radius <= 0.5) { 261 | fn = drawModuleRounded; 262 | } 263 | 264 | context.beginPath(); 265 | for (row = 0; row < moduleCount; row += 1) { 266 | for (col = 0; col < moduleCount; col += 1) { 267 | 268 | var l = settings.left + col * moduleSize, 269 | t = settings.top + row * moduleSize, 270 | w = moduleSize; 271 | 272 | fn(qr, context, settings, l, t, w, row, col); 273 | } 274 | } 275 | if ($(settings.fill).is('img')) { 276 | context.strokeStyle = 'rgba(0,0,0,0.5)'; 277 | context.lineWidth = 2; 278 | context.stroke(); 279 | var prev = context.globalCompositeOperation; 280 | context.globalCompositeOperation = "destination-out"; 281 | context.fill(); 282 | context.globalCompositeOperation = prev; 283 | 284 | context.clip(); 285 | context.drawImage(settings.fill, 0, 0, settings.size, settings.size); 286 | context.restore(); 287 | } else { 288 | context.fillStyle = settings.fill; 289 | context.fill(); 290 | } 291 | }, 292 | 293 | // Draws QR code to the given `canvas` and returns it. 294 | drawOnCanvas = function (canvas, settings) { 295 | 296 | var qr = createQRCode(settings.text, settings.ecLevel, settings.minVersion, settings.maxVersion, settings.quiet); 297 | if (!qr) { 298 | return null; 299 | } 300 | 301 | var $canvas = $(canvas).data('qrcode', qr), 302 | context = $canvas[0].getContext('2d'); 303 | 304 | drawBackground(qr, context, settings); 305 | drawModules(qr, context, settings); 306 | 307 | return $canvas; 308 | }, 309 | 310 | // Returns a `canvas` element representing the QR code for the given settings. 311 | createCanvas = function (settings) { 312 | 313 | var $canvas = $('').attr('width', settings.size).attr('height', settings.size); 314 | return drawOnCanvas($canvas, settings); 315 | }, 316 | 317 | // Returns an `image` element representing the QR code for the given settings. 318 | createImage = function (settings) { 319 | 320 | return $('').attr('src', createCanvas(settings)[0].toDataURL('image/png')); 321 | }, 322 | 323 | // Returns a `div` element representing the QR code for the given settings. 324 | createDiv = function (settings) { 325 | 326 | var qr = createQRCode(settings.text, settings.ecLevel, settings.minVersion, settings.maxVersion, settings.quiet); 327 | if (!qr) { 328 | return null; 329 | } 330 | 331 | // some shortcuts to improve compression 332 | var settings_size = settings.size, 333 | settings_bgColor = settings.background, 334 | math_floor = Math.floor, 335 | 336 | moduleCount = qr.moduleCount, 337 | moduleSize = math_floor(settings_size / moduleCount), 338 | offset = math_floor(0.5 * (settings_size - moduleSize * moduleCount)), 339 | 340 | row, col, 341 | 342 | containerCSS = { 343 | position: 'relative', 344 | left: 0, 345 | top: 0, 346 | padding: 0, 347 | margin: 0, 348 | width: settings_size, 349 | height: settings_size 350 | }, 351 | darkCSS = { 352 | position: 'absolute', 353 | padding: 0, 354 | margin: 0, 355 | width: moduleSize, 356 | height: moduleSize, 357 | 'background-color': settings.fill 358 | }, 359 | 360 | $div = $('
').data('qrcode', qr).css(containerCSS); 361 | 362 | if (settings_bgColor) { 363 | $div.css('background-color', settings_bgColor); 364 | } 365 | 366 | for (row = 0; row < moduleCount; row += 1) { 367 | for (col = 0; col < moduleCount; col += 1) { 368 | if (qr.isDark(row, col)) { 369 | $('
') 370 | .css(darkCSS) 371 | .css({ 372 | left: offset + col * moduleSize, 373 | top: offset + row * moduleSize 374 | }) 375 | .appendTo($div); 376 | } 377 | } 378 | } 379 | 380 | return $div; 381 | }, 382 | 383 | createHTML = function (settings) { 384 | 385 | if (canvasAvailable && settings.render === 'canvas') { 386 | return createCanvas(settings); 387 | } else if (canvasAvailable && settings.render === 'image') { 388 | return createImage(settings); 389 | } 390 | 391 | return createDiv(settings); 392 | }, 393 | 394 | // Plugin 395 | // ====== 396 | 397 | // Default settings 398 | // ---------------- 399 | defaults = { 400 | 401 | // render method: `'canvas'`, `'image'` or `'div'` 402 | render: 'canvas', 403 | 404 | // version range somewhere in 1 .. 40 405 | minVersion: 1, 406 | maxVersion: 40, 407 | 408 | // error correction level: `'L'`, `'M'`, `'Q'` or `'H'` 409 | ecLevel: 'L', 410 | 411 | // offset in pixel if drawn onto existing canvas 412 | left: 0, 413 | top: 0, 414 | 415 | // size in pixel 416 | size: 200, 417 | 418 | // code color or image element 419 | fill: '#000', 420 | 421 | // background color or image element, `null` for transparent background 422 | background: null, 423 | 424 | // content 425 | text: 'no text', 426 | 427 | // corner radius relative to module width: 0.0 .. 0.5 428 | radius: 0, 429 | 430 | // quiet zone in modules 431 | quiet: 0, 432 | 433 | // modes 434 | // 0: normal 435 | // 1: label strip 436 | // 2: label box 437 | // 3: image strip 438 | // 4: image box 439 | mode: 0, 440 | 441 | mSize: 0.1, 442 | mPosX: 0.5, 443 | mPosY: 0.5, 444 | 445 | label: 'no label', 446 | fontname: 'sans', 447 | fontcolor: '#000', 448 | 449 | image: null 450 | }; 451 | 452 | // Register the plugin 453 | // ------------------- 454 | $.fn.qrcode = function(options) { 455 | 456 | var settings = $.extend({}, defaults, options); 457 | 458 | return this.each(function () { 459 | 460 | if (this.nodeName.toLowerCase() === 'canvas') { 461 | drawOnCanvas(this, settings); 462 | } else { 463 | $(this).append(createHTML(settings)); 464 | } 465 | }); 466 | }; 467 | 468 | // jQuery.qrcode plug in code ends here 469 | 470 | // QR Code Generator 471 | // ================= 472 | // @include "qrcode.js" 473 | 474 | }(jQuery)); 475 | -------------------------------------------------------------------------------- /src/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 | var qrcode = function() { 19 | 20 | //--------------------------------------------------------------------- 21 | // qrcode 22 | //--------------------------------------------------------------------- 23 | 24 | /** 25 | * qrcode 26 | * @param typeNumber 1 to 10 27 | * @param errorCorrectLevel 'L','M','Q','H' 28 | */ 29 | var qrcode = function(typeNumber, errorCorrectLevel) { 30 | 31 | var PAD0 = 0xEC; 32 | var PAD1 = 0x11; 33 | 34 | var _typeNumber = typeNumber; 35 | var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel]; 36 | var _modules = null; 37 | var _moduleCount = 0; 38 | var _dataCache = null; 39 | var _dataList = new Array(); 40 | 41 | var _this = {}; 42 | 43 | var makeImpl = function(test, maskPattern) { 44 | 45 | _moduleCount = _typeNumber * 4 + 17; 46 | _modules = function(moduleCount) { 47 | var modules = new Array(moduleCount); 48 | for (var row = 0; row < moduleCount; row += 1) { 49 | modules[row] = new Array(moduleCount); 50 | for (var col = 0; col < moduleCount; col += 1) { 51 | modules[row][col] = null; 52 | } 53 | } 54 | return modules; 55 | }(_moduleCount); 56 | 57 | setupPositionProbePattern(0, 0); 58 | setupPositionProbePattern(_moduleCount - 7, 0); 59 | setupPositionProbePattern(0, _moduleCount - 7); 60 | setupPositionAdjustPattern(); 61 | setupTimingPattern(); 62 | setupTypeInfo(test, maskPattern); 63 | 64 | if (_typeNumber >= 7) { 65 | setupTypeNumber(test); 66 | } 67 | 68 | if (_dataCache == null) { 69 | _dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList); 70 | } 71 | 72 | mapData(_dataCache, maskPattern); 73 | }; 74 | 75 | var setupPositionProbePattern = function(row, col) { 76 | 77 | for (var r = -1; r <= 7; r += 1) { 78 | 79 | if (row + r <= -1 || _moduleCount <= row + r) continue; 80 | 81 | for (var c = -1; c <= 7; c += 1) { 82 | 83 | if (col + c <= -1 || _moduleCount <= col + c) continue; 84 | 85 | if ( (0 <= r && r <= 6 && (c == 0 || c == 6) ) 86 | || (0 <= c && c <= 6 && (r == 0 || r == 6) ) 87 | || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { 88 | _modules[row + r][col + c] = true; 89 | } else { 90 | _modules[row + r][col + c] = false; 91 | } 92 | } 93 | } 94 | }; 95 | 96 | var getBestMaskPattern = function() { 97 | 98 | var minLostPoint = 0; 99 | var pattern = 0; 100 | 101 | for (var i = 0; i < 8; i += 1) { 102 | 103 | makeImpl(true, i); 104 | 105 | var lostPoint = QRUtil.getLostPoint(_this); 106 | 107 | if (i == 0 || minLostPoint > lostPoint) { 108 | minLostPoint = lostPoint; 109 | pattern = i; 110 | } 111 | } 112 | 113 | return pattern; 114 | }; 115 | 116 | var setupTimingPattern = function() { 117 | 118 | for (var r = 8; r < _moduleCount - 8; r += 1) { 119 | if (_modules[r][6] != null) { 120 | continue; 121 | } 122 | _modules[r][6] = (r % 2 == 0); 123 | } 124 | 125 | for (var c = 8; c < _moduleCount - 8; c += 1) { 126 | if (_modules[6][c] != null) { 127 | continue; 128 | } 129 | _modules[6][c] = (c % 2 == 0); 130 | } 131 | }; 132 | 133 | var setupPositionAdjustPattern = function() { 134 | 135 | var pos = QRUtil.getPatternPosition(_typeNumber); 136 | 137 | for (var i = 0; i < pos.length; i += 1) { 138 | 139 | for (var j = 0; j < pos.length; j += 1) { 140 | 141 | var row = pos[i]; 142 | var col = pos[j]; 143 | 144 | if (_modules[row][col] != null) { 145 | continue; 146 | } 147 | 148 | for (var r = -2; r <= 2; r += 1) { 149 | 150 | for (var c = -2; c <= 2; c += 1) { 151 | 152 | if (r == -2 || r == 2 || c == -2 || c == 2 153 | || (r == 0 && c == 0) ) { 154 | _modules[row + r][col + c] = true; 155 | } else { 156 | _modules[row + r][col + c] = false; 157 | } 158 | } 159 | } 160 | } 161 | } 162 | }; 163 | 164 | var setupTypeNumber = function(test) { 165 | 166 | var bits = QRUtil.getBCHTypeNumber(_typeNumber); 167 | 168 | for (var i = 0; i < 18; i += 1) { 169 | var mod = (!test && ( (bits >> i) & 1) == 1); 170 | _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; 171 | } 172 | 173 | for (var i = 0; i < 18; i += 1) { 174 | var mod = (!test && ( (bits >> i) & 1) == 1); 175 | _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; 176 | } 177 | }; 178 | 179 | var setupTypeInfo = function(test, maskPattern) { 180 | 181 | var data = (_errorCorrectLevel << 3) | maskPattern; 182 | var bits = QRUtil.getBCHTypeInfo(data); 183 | 184 | // vertical 185 | for (var i = 0; i < 15; i += 1) { 186 | 187 | var mod = (!test && ( (bits >> i) & 1) == 1); 188 | 189 | if (i < 6) { 190 | _modules[i][8] = mod; 191 | } else if (i < 8) { 192 | _modules[i + 1][8] = mod; 193 | } else { 194 | _modules[_moduleCount - 15 + i][8] = mod; 195 | } 196 | } 197 | 198 | // horizontal 199 | for (var i = 0; i < 15; i += 1) { 200 | 201 | var mod = (!test && ( (bits >> i) & 1) == 1); 202 | 203 | if (i < 8) { 204 | _modules[8][_moduleCount - i - 1] = mod; 205 | } else if (i < 9) { 206 | _modules[8][15 - i - 1 + 1] = mod; 207 | } else { 208 | _modules[8][15 - i - 1] = mod; 209 | } 210 | } 211 | 212 | // fixed module 213 | _modules[_moduleCount - 8][8] = (!test); 214 | }; 215 | 216 | var mapData = function(data, maskPattern) { 217 | 218 | var inc = -1; 219 | var row = _moduleCount - 1; 220 | var bitIndex = 7; 221 | var byteIndex = 0; 222 | var maskFunc = QRUtil.getMaskFunction(maskPattern); 223 | 224 | for (var col = _moduleCount - 1; col > 0; col -= 2) { 225 | 226 | if (col == 6) col -= 1; 227 | 228 | while (true) { 229 | 230 | for (var c = 0; c < 2; c += 1) { 231 | 232 | if (_modules[row][col - c] == null) { 233 | 234 | var dark = false; 235 | 236 | if (byteIndex < data.length) { 237 | dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1); 238 | } 239 | 240 | var mask = maskFunc(row, col - c); 241 | 242 | if (mask) { 243 | dark = !dark; 244 | } 245 | 246 | _modules[row][col - c] = dark; 247 | bitIndex -= 1; 248 | 249 | if (bitIndex == -1) { 250 | byteIndex += 1; 251 | bitIndex = 7; 252 | } 253 | } 254 | } 255 | 256 | row += inc; 257 | 258 | if (row < 0 || _moduleCount <= row) { 259 | row -= inc; 260 | inc = -inc; 261 | break; 262 | } 263 | } 264 | } 265 | }; 266 | 267 | var createBytes = function(buffer, rsBlocks) { 268 | 269 | var offset = 0; 270 | 271 | var maxDcCount = 0; 272 | var maxEcCount = 0; 273 | 274 | var dcdata = new Array(rsBlocks.length); 275 | var ecdata = new Array(rsBlocks.length); 276 | 277 | for (var r = 0; r < rsBlocks.length; r += 1) { 278 | 279 | var dcCount = rsBlocks[r].dataCount; 280 | var ecCount = rsBlocks[r].totalCount - dcCount; 281 | 282 | maxDcCount = Math.max(maxDcCount, dcCount); 283 | maxEcCount = Math.max(maxEcCount, ecCount); 284 | 285 | dcdata[r] = new Array(dcCount); 286 | 287 | for (var i = 0; i < dcdata[r].length; i += 1) { 288 | dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; 289 | } 290 | offset += dcCount; 291 | 292 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount); 293 | var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1); 294 | 295 | var modPoly = rawPoly.mod(rsPoly); 296 | ecdata[r] = new Array(rsPoly.getLength() - 1); 297 | for (var i = 0; i < ecdata[r].length; i += 1) { 298 | var modIndex = i + modPoly.getLength() - ecdata[r].length; 299 | ecdata[r][i] = (modIndex >= 0)? modPoly.get(modIndex) : 0; 300 | } 301 | } 302 | 303 | var totalCodeCount = 0; 304 | for (var i = 0; i < rsBlocks.length; i += 1) { 305 | totalCodeCount += rsBlocks[i].totalCount; 306 | } 307 | 308 | var data = new Array(totalCodeCount); 309 | var index = 0; 310 | 311 | for (var i = 0; i < maxDcCount; i += 1) { 312 | for (var r = 0; r < rsBlocks.length; r += 1) { 313 | if (i < dcdata[r].length) { 314 | data[index] = dcdata[r][i]; 315 | index += 1; 316 | } 317 | } 318 | } 319 | 320 | for (var i = 0; i < maxEcCount; i += 1) { 321 | for (var r = 0; r < rsBlocks.length; r += 1) { 322 | if (i < ecdata[r].length) { 323 | data[index] = ecdata[r][i]; 324 | index += 1; 325 | } 326 | } 327 | } 328 | 329 | return data; 330 | }; 331 | 332 | var createData = function(typeNumber, errorCorrectLevel, dataList) { 333 | 334 | var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); 335 | 336 | var buffer = qrBitBuffer(); 337 | 338 | for (var i = 0; i < dataList.length; i += 1) { 339 | var data = dataList[i]; 340 | buffer.put(data.getMode(), 4); 341 | buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); 342 | data.write(buffer); 343 | } 344 | 345 | // calc num max data. 346 | var totalDataCount = 0; 347 | for (var i = 0; i < rsBlocks.length; i += 1) { 348 | totalDataCount += rsBlocks[i].dataCount; 349 | } 350 | 351 | if (buffer.getLengthInBits() > totalDataCount * 8) { 352 | throw new Error('code length overflow. (' 353 | + buffer.getLengthInBits() 354 | + '>' 355 | + totalDataCount * 8 356 | + ')'); 357 | } 358 | 359 | // end code 360 | if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { 361 | buffer.put(0, 4); 362 | } 363 | 364 | // padding 365 | while (buffer.getLengthInBits() % 8 != 0) { 366 | buffer.putBit(false); 367 | } 368 | 369 | // padding 370 | while (true) { 371 | 372 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 373 | break; 374 | } 375 | buffer.put(PAD0, 8); 376 | 377 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 378 | break; 379 | } 380 | buffer.put(PAD1, 8); 381 | } 382 | 383 | return createBytes(buffer, rsBlocks); 384 | }; 385 | 386 | _this.addData = function(data) { 387 | var newData = qr8BitByte(data); 388 | _dataList.push(newData); 389 | _dataCache = null; 390 | }; 391 | 392 | _this.isDark = function(row, col) { 393 | if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) { 394 | throw new Error(row + ',' + col); 395 | } 396 | return _modules[row][col]; 397 | }; 398 | 399 | _this.getModuleCount = function() { 400 | return _moduleCount; 401 | }; 402 | 403 | _this.make = function() { 404 | makeImpl(false, getBestMaskPattern() ); 405 | }; 406 | 407 | _this.createTableTag = function(cellSize, margin) { 408 | 409 | cellSize = cellSize || 2; 410 | margin = (typeof margin == 'undefined')? cellSize * 4 : margin; 411 | 412 | var qrHtml = ''; 413 | 414 | qrHtml += ''; 419 | qrHtml += ''; 420 | 421 | for (var r = 0; r < _this.getModuleCount(); r += 1) { 422 | 423 | qrHtml += ''; 424 | 425 | for (var c = 0; c < _this.getModuleCount(); c += 1) { 426 | qrHtml += ''; 439 | } 440 | 441 | qrHtml += ''; 442 | qrHtml += '
'; 436 | } 437 | 438 | qrHtml += '
'; 443 | 444 | return qrHtml; 445 | }; 446 | 447 | _this.createImgTag = function(cellSize, margin) { 448 | 449 | cellSize = cellSize || 2; 450 | margin = (typeof margin == 'undefined')? cellSize * 4 : margin; 451 | 452 | var size = _this.getModuleCount() * cellSize + margin * 2; 453 | var min = margin; 454 | var max = size - margin; 455 | 456 | return createImgTag(size, size, function(x, y) { 457 | if (min <= x && x < max && min <= y && y < max) { 458 | var c = Math.floor( (x - min) / cellSize); 459 | var r = Math.floor( (y - min) / cellSize); 460 | return _this.isDark(r, c)? 0 : 1; 461 | } else { 462 | return 1; 463 | } 464 | } ); 465 | }; 466 | 467 | return _this; 468 | }; 469 | 470 | //--------------------------------------------------------------------- 471 | // qrcode.stringToBytes 472 | //--------------------------------------------------------------------- 473 | 474 | qrcode.stringToBytes = function(s) { 475 | var bytes = new Array(); 476 | for (var i = 0; i < s.length; i += 1) { 477 | var c = s.charCodeAt(i); 478 | bytes.push(c & 0xff); 479 | } 480 | return bytes; 481 | }; 482 | 483 | //--------------------------------------------------------------------- 484 | // qrcode.createStringToBytes 485 | //--------------------------------------------------------------------- 486 | 487 | /** 488 | * @param unicodeData base64 string of byte array. 489 | * [16bit Unicode],[16bit Bytes], ... 490 | * @param numChars 491 | */ 492 | qrcode.createStringToBytes = function(unicodeData, numChars) { 493 | 494 | // create conversion map. 495 | 496 | var unicodeMap = function() { 497 | 498 | var bin = base64DecodeInputStream(unicodeData); 499 | var read = function() { 500 | var b = bin.read(); 501 | if (b == -1) throw new Error(); 502 | return b; 503 | }; 504 | 505 | var count = 0; 506 | var unicodeMap = {}; 507 | while (true) { 508 | var b0 = bin.read(); 509 | if (b0 == -1) break; 510 | var b1 = read(); 511 | var b2 = read(); 512 | var b3 = read(); 513 | var k = String.fromCharCode( (b0 << 8) | b1); 514 | var v = (b2 << 8) | b3; 515 | unicodeMap[k] = v; 516 | count += 1; 517 | } 518 | if (count != numChars) { 519 | throw new Error(count + ' != ' + numChars); 520 | } 521 | 522 | return unicodeMap; 523 | }(); 524 | 525 | var unknownChar = '?'.charCodeAt(0); 526 | 527 | return function(s) { 528 | var bytes = new Array(); 529 | for (var i = 0; i < s.length; i += 1) { 530 | var c = s.charCodeAt(i); 531 | if (c < 128) { 532 | bytes.push(c); 533 | } else { 534 | var b = unicodeMap[s.charAt(i)]; 535 | if (typeof b == 'number') { 536 | if ( (b & 0xff) == b) { 537 | // 1byte 538 | bytes.push(b); 539 | } else { 540 | // 2bytes 541 | bytes.push(b >>> 8); 542 | bytes.push(b & 0xff); 543 | } 544 | } else { 545 | bytes.push(unknownChar); 546 | } 547 | } 548 | } 549 | return bytes; 550 | }; 551 | }; 552 | 553 | //--------------------------------------------------------------------- 554 | // QRMode 555 | //--------------------------------------------------------------------- 556 | 557 | var QRMode = { 558 | MODE_NUMBER : 1 << 0, 559 | MODE_ALPHA_NUM : 1 << 1, 560 | MODE_8BIT_BYTE : 1 << 2, 561 | MODE_KANJI : 1 << 3 562 | }; 563 | 564 | //--------------------------------------------------------------------- 565 | // QRErrorCorrectLevel 566 | //--------------------------------------------------------------------- 567 | 568 | var QRErrorCorrectLevel = { 569 | L : 1, 570 | M : 0, 571 | Q : 3, 572 | H : 2 573 | }; 574 | 575 | //--------------------------------------------------------------------- 576 | // QRMaskPattern 577 | //--------------------------------------------------------------------- 578 | 579 | var QRMaskPattern = { 580 | PATTERN000 : 0, 581 | PATTERN001 : 1, 582 | PATTERN010 : 2, 583 | PATTERN011 : 3, 584 | PATTERN100 : 4, 585 | PATTERN101 : 5, 586 | PATTERN110 : 6, 587 | PATTERN111 : 7 588 | }; 589 | 590 | //--------------------------------------------------------------------- 591 | // QRUtil 592 | //--------------------------------------------------------------------- 593 | 594 | var QRUtil = function() { 595 | 596 | var PATTERN_POSITION_TABLE = [ 597 | [], 598 | [6, 18], 599 | [6, 22], 600 | [6, 26], 601 | [6, 30], 602 | [6, 34], 603 | [6, 22, 38], 604 | [6, 24, 42], 605 | [6, 26, 46], 606 | [6, 28, 50], 607 | [6, 30, 54], 608 | [6, 32, 58], 609 | [6, 34, 62], 610 | [6, 26, 46, 66], 611 | [6, 26, 48, 70], 612 | [6, 26, 50, 74], 613 | [6, 30, 54, 78], 614 | [6, 30, 56, 82], 615 | [6, 30, 58, 86], 616 | [6, 34, 62, 90], 617 | [6, 28, 50, 72, 94], 618 | [6, 26, 50, 74, 98], 619 | [6, 30, 54, 78, 102], 620 | [6, 28, 54, 80, 106], 621 | [6, 32, 58, 84, 110], 622 | [6, 30, 58, 86, 114], 623 | [6, 34, 62, 90, 118], 624 | [6, 26, 50, 74, 98, 122], 625 | [6, 30, 54, 78, 102, 126], 626 | [6, 26, 52, 78, 104, 130], 627 | [6, 30, 56, 82, 108, 134], 628 | [6, 34, 60, 86, 112, 138], 629 | [6, 30, 58, 86, 114, 142], 630 | [6, 34, 62, 90, 118, 146], 631 | [6, 30, 54, 78, 102, 126, 150], 632 | [6, 24, 50, 76, 102, 128, 154], 633 | [6, 28, 54, 80, 106, 132, 158], 634 | [6, 32, 58, 84, 110, 136, 162], 635 | [6, 26, 54, 82, 110, 138, 166], 636 | [6, 30, 58, 86, 114, 142, 170] 637 | ]; 638 | var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); 639 | var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); 640 | var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); 641 | 642 | var _this = {}; 643 | 644 | var getBCHDigit = function(data) { 645 | var digit = 0; 646 | while (data != 0) { 647 | digit += 1; 648 | data >>>= 1; 649 | } 650 | return digit; 651 | }; 652 | 653 | _this.getBCHTypeInfo = function(data) { 654 | var d = data << 10; 655 | while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { 656 | d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) ); 657 | } 658 | return ( (data << 10) | d) ^ G15_MASK; 659 | }; 660 | 661 | _this.getBCHTypeNumber = function(data) { 662 | var d = data << 12; 663 | while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { 664 | d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) ); 665 | } 666 | return (data << 12) | d; 667 | }; 668 | 669 | _this.getPatternPosition = function(typeNumber) { 670 | return PATTERN_POSITION_TABLE[typeNumber - 1]; 671 | }; 672 | 673 | _this.getMaskFunction = function(maskPattern) { 674 | 675 | switch (maskPattern) { 676 | 677 | case QRMaskPattern.PATTERN000 : 678 | return function(i, j) { return (i + j) % 2 == 0; }; 679 | case QRMaskPattern.PATTERN001 : 680 | return function(i, j) { return i % 2 == 0; }; 681 | case QRMaskPattern.PATTERN010 : 682 | return function(i, j) { return j % 3 == 0; }; 683 | case QRMaskPattern.PATTERN011 : 684 | return function(i, j) { return (i + j) % 3 == 0; }; 685 | case QRMaskPattern.PATTERN100 : 686 | return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; }; 687 | case QRMaskPattern.PATTERN101 : 688 | return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; }; 689 | case QRMaskPattern.PATTERN110 : 690 | return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; }; 691 | case QRMaskPattern.PATTERN111 : 692 | return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; }; 693 | 694 | default : 695 | throw new Error('bad maskPattern:' + maskPattern); 696 | } 697 | }; 698 | 699 | _this.getErrorCorrectPolynomial = function(errorCorrectLength) { 700 | var a = qrPolynomial([1], 0); 701 | for (var i = 0; i < errorCorrectLength; i += 1) { 702 | a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) ); 703 | } 704 | return a; 705 | }; 706 | 707 | _this.getLengthInBits = function(mode, type) { 708 | 709 | if (1 <= type && type < 10) { 710 | 711 | // 1 - 9 712 | 713 | switch(mode) { 714 | case QRMode.MODE_NUMBER : return 10; 715 | case QRMode.MODE_ALPHA_NUM : return 9; 716 | case QRMode.MODE_8BIT_BYTE : return 8; 717 | case QRMode.MODE_KANJI : return 8; 718 | default : 719 | throw new Error('mode:' + mode); 720 | } 721 | 722 | } else if (type < 27) { 723 | 724 | // 10 - 26 725 | 726 | switch(mode) { 727 | case QRMode.MODE_NUMBER : return 12; 728 | case QRMode.MODE_ALPHA_NUM : return 11; 729 | case QRMode.MODE_8BIT_BYTE : return 16; 730 | case QRMode.MODE_KANJI : return 10; 731 | default : 732 | throw new Error('mode:' + mode); 733 | } 734 | 735 | } else if (type < 41) { 736 | 737 | // 27 - 40 738 | 739 | switch(mode) { 740 | case QRMode.MODE_NUMBER : return 14; 741 | case QRMode.MODE_ALPHA_NUM : return 13; 742 | case QRMode.MODE_8BIT_BYTE : return 16; 743 | case QRMode.MODE_KANJI : return 12; 744 | default : 745 | throw new Error('mode:' + mode); 746 | } 747 | 748 | } else { 749 | throw new Error('type:' + type); 750 | } 751 | }; 752 | 753 | _this.getLostPoint = function(qrcode) { 754 | 755 | var moduleCount = qrcode.getModuleCount(); 756 | 757 | var lostPoint = 0; 758 | 759 | // LEVEL1 760 | 761 | for (var row = 0; row < moduleCount; row += 1) { 762 | for (var col = 0; col < moduleCount; col += 1) { 763 | 764 | var sameCount = 0; 765 | var dark = qrcode.isDark(row, col); 766 | 767 | for (var r = -1; r <= 1; r += 1) { 768 | 769 | if (row + r < 0 || moduleCount <= row + r) { 770 | continue; 771 | } 772 | 773 | for (var c = -1; c <= 1; c += 1) { 774 | 775 | if (col + c < 0 || moduleCount <= col + c) { 776 | continue; 777 | } 778 | 779 | if (r == 0 && c == 0) { 780 | continue; 781 | } 782 | 783 | if (dark == qrcode.isDark(row + r, col + c) ) { 784 | sameCount += 1; 785 | } 786 | } 787 | } 788 | 789 | if (sameCount > 5) { 790 | lostPoint += (3 + sameCount - 5); 791 | } 792 | } 793 | }; 794 | 795 | // LEVEL2 796 | 797 | for (var row = 0; row < moduleCount - 1; row += 1) { 798 | for (var col = 0; col < moduleCount - 1; col += 1) { 799 | var count = 0; 800 | if (qrcode.isDark(row, col) ) count += 1; 801 | if (qrcode.isDark(row + 1, col) ) count += 1; 802 | if (qrcode.isDark(row, col + 1) ) count += 1; 803 | if (qrcode.isDark(row + 1, col + 1) ) count += 1; 804 | if (count == 0 || count == 4) { 805 | lostPoint += 3; 806 | } 807 | } 808 | } 809 | 810 | // LEVEL3 811 | 812 | for (var row = 0; row < moduleCount; row += 1) { 813 | for (var col = 0; col < moduleCount - 6; col += 1) { 814 | if (qrcode.isDark(row, col) 815 | && !qrcode.isDark(row, col + 1) 816 | && qrcode.isDark(row, col + 2) 817 | && qrcode.isDark(row, col + 3) 818 | && qrcode.isDark(row, col + 4) 819 | && !qrcode.isDark(row, col + 5) 820 | && qrcode.isDark(row, col + 6) ) { 821 | lostPoint += 40; 822 | } 823 | } 824 | } 825 | 826 | for (var col = 0; col < moduleCount; col += 1) { 827 | for (var row = 0; row < moduleCount - 6; row += 1) { 828 | if (qrcode.isDark(row, col) 829 | && !qrcode.isDark(row + 1, col) 830 | && qrcode.isDark(row + 2, col) 831 | && qrcode.isDark(row + 3, col) 832 | && qrcode.isDark(row + 4, col) 833 | && !qrcode.isDark(row + 5, col) 834 | && qrcode.isDark(row + 6, col) ) { 835 | lostPoint += 40; 836 | } 837 | } 838 | } 839 | 840 | // LEVEL4 841 | 842 | var darkCount = 0; 843 | 844 | for (var col = 0; col < moduleCount; col += 1) { 845 | for (var row = 0; row < moduleCount; row += 1) { 846 | if (qrcode.isDark(row, col) ) { 847 | darkCount += 1; 848 | } 849 | } 850 | } 851 | 852 | var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; 853 | lostPoint += ratio * 10; 854 | 855 | return lostPoint; 856 | }; 857 | 858 | return _this; 859 | }(); 860 | 861 | //--------------------------------------------------------------------- 862 | // QRMath 863 | //--------------------------------------------------------------------- 864 | 865 | var QRMath = function() { 866 | 867 | var EXP_TABLE = new Array(256); 868 | var LOG_TABLE = new Array(256); 869 | 870 | // initialize tables 871 | for (var i = 0; i < 8; i += 1) { 872 | EXP_TABLE[i] = 1 << i; 873 | } 874 | for (var i = 8; i < 256; i += 1) { 875 | EXP_TABLE[i] = EXP_TABLE[i - 4] 876 | ^ EXP_TABLE[i - 5] 877 | ^ EXP_TABLE[i - 6] 878 | ^ EXP_TABLE[i - 8]; 879 | } 880 | for (var i = 0; i < 255; i += 1) { 881 | LOG_TABLE[EXP_TABLE[i] ] = i; 882 | } 883 | 884 | var _this = {}; 885 | 886 | _this.glog = function(n) { 887 | 888 | if (n < 1) { 889 | throw new Error('glog(' + n + ')'); 890 | } 891 | 892 | return LOG_TABLE[n]; 893 | }; 894 | 895 | _this.gexp = function(n) { 896 | 897 | while (n < 0) { 898 | n += 255; 899 | } 900 | 901 | while (n >= 256) { 902 | n -= 255; 903 | } 904 | 905 | return EXP_TABLE[n]; 906 | }; 907 | 908 | return _this; 909 | }(); 910 | 911 | //--------------------------------------------------------------------- 912 | // qrPolynomial 913 | //--------------------------------------------------------------------- 914 | 915 | function qrPolynomial(num, shift) { 916 | 917 | if (typeof num.length == 'undefined') { 918 | throw new Error(num.length + '/' + shift); 919 | } 920 | 921 | var _num = function() { 922 | var offset = 0; 923 | while (offset < num.length && num[offset] == 0) { 924 | offset += 1; 925 | } 926 | var _num = new Array(num.length - offset + shift); 927 | for (var i = 0; i < num.length - offset; i += 1) { 928 | _num[i] = num[i + offset]; 929 | } 930 | return _num; 931 | }(); 932 | 933 | var _this = {}; 934 | 935 | _this.get = function(index) { 936 | return _num[index]; 937 | }; 938 | 939 | _this.getLength = function() { 940 | return _num.length; 941 | }; 942 | 943 | _this.multiply = function(e) { 944 | 945 | var num = new Array(_this.getLength() + e.getLength() - 1); 946 | 947 | for (var i = 0; i < _this.getLength(); i += 1) { 948 | for (var j = 0; j < e.getLength(); j += 1) { 949 | num[i + j] ^= QRMath.gexp(QRMath.glog(_this.get(i) ) + QRMath.glog(e.get(j) ) ); 950 | } 951 | } 952 | 953 | return qrPolynomial(num, 0); 954 | }; 955 | 956 | _this.mod = function(e) { 957 | 958 | if (_this.getLength() - e.getLength() < 0) { 959 | return _this; 960 | } 961 | 962 | var ratio = QRMath.glog(_this.get(0) ) - QRMath.glog(e.get(0) ); 963 | 964 | var num = new Array(_this.getLength() ); 965 | for (var i = 0; i < _this.getLength(); i += 1) { 966 | num[i] = _this.get(i); 967 | } 968 | 969 | for (var i = 0; i < e.getLength(); i += 1) { 970 | num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio); 971 | } 972 | 973 | // recursive call 974 | return qrPolynomial(num, 0).mod(e); 975 | }; 976 | 977 | return _this; 978 | }; 979 | 980 | //--------------------------------------------------------------------- 981 | // QRRSBlock 982 | //--------------------------------------------------------------------- 983 | 984 | var QRRSBlock = function() { 985 | 986 | var RS_BLOCK_TABLE = [ 987 | 988 | // L 989 | // M 990 | // Q 991 | // H 992 | 993 | // 1 994 | [1, 26, 19], 995 | [1, 26, 16], 996 | [1, 26, 13], 997 | [1, 26, 9], 998 | 999 | // 2 1000 | [1, 44, 34], 1001 | [1, 44, 28], 1002 | [1, 44, 22], 1003 | [1, 44, 16], 1004 | 1005 | // 3 1006 | [1, 70, 55], 1007 | [1, 70, 44], 1008 | [2, 35, 17], 1009 | [2, 35, 13], 1010 | 1011 | // 4 1012 | [1, 100, 80], 1013 | [2, 50, 32], 1014 | [2, 50, 24], 1015 | [4, 25, 9], 1016 | 1017 | // 5 1018 | [1, 134, 108], 1019 | [2, 67, 43], 1020 | [2, 33, 15, 2, 34, 16], 1021 | [2, 33, 11, 2, 34, 12], 1022 | 1023 | // 6 1024 | [2, 86, 68], 1025 | [4, 43, 27], 1026 | [4, 43, 19], 1027 | [4, 43, 15], 1028 | 1029 | // 7 1030 | [2, 98, 78], 1031 | [4, 49, 31], 1032 | [2, 32, 14, 4, 33, 15], 1033 | [4, 39, 13, 1, 40, 14], 1034 | 1035 | // 8 1036 | [2, 121, 97], 1037 | [2, 60, 38, 2, 61, 39], 1038 | [4, 40, 18, 2, 41, 19], 1039 | [4, 40, 14, 2, 41, 15], 1040 | 1041 | // 9 1042 | [2, 146, 116], 1043 | [3, 58, 36, 2, 59, 37], 1044 | [4, 36, 16, 4, 37, 17], 1045 | [4, 36, 12, 4, 37, 13], 1046 | 1047 | // 10 1048 | [2, 86, 68, 2, 87, 69], 1049 | [4, 69, 43, 1, 70, 44], 1050 | [6, 43, 19, 2, 44, 20], 1051 | [6, 43, 15, 2, 44, 16], 1052 | 1053 | // 11 1054 | [4, 101, 81], 1055 | [1, 80, 50, 4, 81, 51], 1056 | [4, 50, 22, 4, 51, 23], 1057 | [3, 36, 12, 8, 37, 13], 1058 | 1059 | // 12 1060 | [2, 116, 92, 2, 117, 93], 1061 | [6, 58, 36, 2, 59, 37], 1062 | [4, 46, 20, 6, 47, 21], 1063 | [7, 42, 14, 4, 43, 15], 1064 | 1065 | // 13 1066 | [4, 133, 107], 1067 | [8, 59, 37, 1, 60, 38], 1068 | [8, 44, 20, 4, 45, 21], 1069 | [12, 33, 11, 4, 34, 12], 1070 | 1071 | // 14 1072 | [3, 145, 115, 1, 146, 116], 1073 | [4, 64, 40, 5, 65, 41], 1074 | [11, 36, 16, 5, 37, 17], 1075 | [11, 36, 12, 5, 37, 13], 1076 | 1077 | // 15 1078 | [5, 109, 87, 1, 110, 88], 1079 | [5, 65, 41, 5, 66, 42], 1080 | [5, 54, 24, 7, 55, 25], 1081 | [11, 36, 12], 1082 | 1083 | // 16 1084 | [5, 122, 98, 1, 123, 99], 1085 | [7, 73, 45, 3, 74, 46], 1086 | [15, 43, 19, 2, 44, 20], 1087 | [3, 45, 15, 13, 46, 16], 1088 | 1089 | // 17 1090 | [1, 135, 107, 5, 136, 108], 1091 | [10, 74, 46, 1, 75, 47], 1092 | [1, 50, 22, 15, 51, 23], 1093 | [2, 42, 14, 17, 43, 15], 1094 | 1095 | // 18 1096 | [5, 150, 120, 1, 151, 121], 1097 | [9, 69, 43, 4, 70, 44], 1098 | [17, 50, 22, 1, 51, 23], 1099 | [2, 42, 14, 19, 43, 15], 1100 | 1101 | // 19 1102 | [3, 141, 113, 4, 142, 114], 1103 | [3, 70, 44, 11, 71, 45], 1104 | [17, 47, 21, 4, 48, 22], 1105 | [9, 39, 13, 16, 40, 14], 1106 | 1107 | // 20 1108 | [3, 135, 107, 5, 136, 108], 1109 | [3, 67, 41, 13, 68, 42], 1110 | [15, 54, 24, 5, 55, 25], 1111 | [15, 43, 15, 10, 44, 16], 1112 | 1113 | // 21 1114 | [4, 144, 116, 4, 145, 117], 1115 | [17, 68, 42], 1116 | [17, 50, 22, 6, 51, 23], 1117 | [19, 46, 16, 6, 47, 17], 1118 | 1119 | // 22 1120 | [2, 139, 111, 7, 140, 112], 1121 | [17, 74, 46], 1122 | [7, 54, 24, 16, 55, 25], 1123 | [34, 37, 13], 1124 | 1125 | // 23 1126 | [4, 151, 121, 5, 152, 122], 1127 | [4, 75, 47, 14, 76, 48], 1128 | [11, 54, 24, 14, 55, 25], 1129 | [16, 45, 15, 14, 46, 16], 1130 | 1131 | // 24 1132 | [6, 147, 117, 4, 148, 118], 1133 | [6, 73, 45, 14, 74, 46], 1134 | [11, 54, 24, 16, 55, 25], 1135 | [30, 46, 16, 2, 47, 17], 1136 | 1137 | // 25 1138 | [8, 132, 106, 4, 133, 107], 1139 | [8, 75, 47, 13, 76, 48], 1140 | [7, 54, 24, 22, 55, 25], 1141 | [22, 45, 15, 13, 46, 16], 1142 | 1143 | // 26 1144 | [10, 142, 114, 2, 143, 115], 1145 | [19, 74, 46, 4, 75, 47], 1146 | [28, 50, 22, 6, 51, 23], 1147 | [33, 46, 16, 4, 47, 17], 1148 | 1149 | // 27 1150 | [8, 152, 122, 4, 153, 123], 1151 | [22, 73, 45, 3, 74, 46], 1152 | [8, 53, 23, 26, 54, 24], 1153 | [12, 45, 15, 28, 46, 16], 1154 | 1155 | // 28 1156 | [3, 147, 117, 10, 148, 118], 1157 | [3, 73, 45, 23, 74, 46], 1158 | [4, 54, 24, 31, 55, 25], 1159 | [11, 45, 15, 31, 46, 16], 1160 | 1161 | // 29 1162 | [7, 146, 116, 7, 147, 117], 1163 | [21, 73, 45, 7, 74, 46], 1164 | [1, 53, 23, 37, 54, 24], 1165 | [19, 45, 15, 26, 46, 16], 1166 | 1167 | // 30 1168 | [5, 145, 115, 10, 146, 116], 1169 | [19, 75, 47, 10, 76, 48], 1170 | [15, 54, 24, 25, 55, 25], 1171 | [23, 45, 15, 25, 46, 16], 1172 | 1173 | // 31 1174 | [13, 145, 115, 3, 146, 116], 1175 | [2, 74, 46, 29, 75, 47], 1176 | [42, 54, 24, 1, 55, 25], 1177 | [23, 45, 15, 28, 46, 16], 1178 | 1179 | // 32 1180 | [17, 145, 115], 1181 | [10, 74, 46, 23, 75, 47], 1182 | [10, 54, 24, 35, 55, 25], 1183 | [19, 45, 15, 35, 46, 16], 1184 | 1185 | // 33 1186 | [17, 145, 115, 1, 146, 116], 1187 | [14, 74, 46, 21, 75, 47], 1188 | [29, 54, 24, 19, 55, 25], 1189 | [11, 45, 15, 46, 46, 16], 1190 | 1191 | // 34 1192 | [13, 145, 115, 6, 146, 116], 1193 | [14, 74, 46, 23, 75, 47], 1194 | [44, 54, 24, 7, 55, 25], 1195 | [59, 46, 16, 1, 47, 17], 1196 | 1197 | // 35 1198 | [12, 151, 121, 7, 152, 122], 1199 | [12, 75, 47, 26, 76, 48], 1200 | [39, 54, 24, 14, 55, 25], 1201 | [22, 45, 15, 41, 46, 16], 1202 | 1203 | // 36 1204 | [6, 151, 121, 14, 152, 122], 1205 | [6, 75, 47, 34, 76, 48], 1206 | [46, 54, 24, 10, 55, 25], 1207 | [2, 45, 15, 64, 46, 16], 1208 | 1209 | // 37 1210 | [17, 152, 122, 4, 153, 123], 1211 | [29, 74, 46, 14, 75, 47], 1212 | [49, 54, 24, 10, 55, 25], 1213 | [24, 45, 15, 46, 46, 16], 1214 | 1215 | // 38 1216 | [4, 152, 122, 18, 153, 123], 1217 | [13, 74, 46, 32, 75, 47], 1218 | [48, 54, 24, 14, 55, 25], 1219 | [42, 45, 15, 32, 46, 16], 1220 | 1221 | // 39 1222 | [20, 147, 117, 4, 148, 118], 1223 | [40, 75, 47, 7, 76, 48], 1224 | [43, 54, 24, 22, 55, 25], 1225 | [10, 45, 15, 67, 46, 16], 1226 | 1227 | // 40 1228 | [19, 148, 118, 6, 149, 119], 1229 | [18, 75, 47, 31, 76, 48], 1230 | [34, 54, 24, 34, 55, 25], 1231 | [20, 45, 15, 61, 46, 16] 1232 | ]; 1233 | 1234 | var qrRSBlock = function(totalCount, dataCount) { 1235 | var _this = {}; 1236 | _this.totalCount = totalCount; 1237 | _this.dataCount = dataCount; 1238 | return _this; 1239 | }; 1240 | 1241 | var _this = {}; 1242 | 1243 | var getRsBlockTable = function(typeNumber, errorCorrectLevel) { 1244 | 1245 | switch(errorCorrectLevel) { 1246 | case QRErrorCorrectLevel.L : 1247 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; 1248 | case QRErrorCorrectLevel.M : 1249 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; 1250 | case QRErrorCorrectLevel.Q : 1251 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; 1252 | case QRErrorCorrectLevel.H : 1253 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; 1254 | default : 1255 | return undefined; 1256 | } 1257 | }; 1258 | 1259 | _this.getRSBlocks = function(typeNumber, errorCorrectLevel) { 1260 | 1261 | var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel); 1262 | 1263 | if (typeof rsBlock == 'undefined') { 1264 | throw new Error('bad rs block @ typeNumber:' + typeNumber + 1265 | '/errorCorrectLevel:' + errorCorrectLevel); 1266 | } 1267 | 1268 | var length = rsBlock.length / 3; 1269 | 1270 | var list = new Array(); 1271 | 1272 | for (var i = 0; i < length; i += 1) { 1273 | 1274 | var count = rsBlock[i * 3 + 0]; 1275 | var totalCount = rsBlock[i * 3 + 1]; 1276 | var dataCount = rsBlock[i * 3 + 2]; 1277 | 1278 | for (var j = 0; j < count; j += 1) { 1279 | list.push(qrRSBlock(totalCount, dataCount) ); 1280 | } 1281 | } 1282 | 1283 | return list; 1284 | }; 1285 | 1286 | return _this; 1287 | }(); 1288 | 1289 | //--------------------------------------------------------------------- 1290 | // qrBitBuffer 1291 | //--------------------------------------------------------------------- 1292 | 1293 | var qrBitBuffer = function() { 1294 | 1295 | var _buffer = new Array(); 1296 | var _length = 0; 1297 | 1298 | var _this = {}; 1299 | 1300 | _this.getBuffer = function() { 1301 | return _buffer; 1302 | }; 1303 | 1304 | _this.get = function(index) { 1305 | var bufIndex = Math.floor(index / 8); 1306 | return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1; 1307 | }; 1308 | 1309 | _this.put = function(num, length) { 1310 | for (var i = 0; i < length; i += 1) { 1311 | _this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1); 1312 | } 1313 | }; 1314 | 1315 | _this.getLengthInBits = function() { 1316 | return _length; 1317 | }; 1318 | 1319 | _this.putBit = function(bit) { 1320 | 1321 | var bufIndex = Math.floor(_length / 8); 1322 | if (_buffer.length <= bufIndex) { 1323 | _buffer.push(0); 1324 | } 1325 | 1326 | if (bit) { 1327 | _buffer[bufIndex] |= (0x80 >>> (_length % 8) ); 1328 | } 1329 | 1330 | _length += 1; 1331 | }; 1332 | 1333 | return _this; 1334 | }; 1335 | 1336 | //--------------------------------------------------------------------- 1337 | // qr8BitByte 1338 | //--------------------------------------------------------------------- 1339 | 1340 | var qr8BitByte = function(data) { 1341 | 1342 | var _mode = QRMode.MODE_8BIT_BYTE; 1343 | var _data = data; 1344 | var _bytes = qrcode.stringToBytes(data); 1345 | 1346 | var _this = {}; 1347 | 1348 | _this.getMode = function() { 1349 | return _mode; 1350 | }; 1351 | 1352 | _this.getLength = function(buffer) { 1353 | return _bytes.length; 1354 | }; 1355 | 1356 | _this.write = function(buffer) { 1357 | for (var i = 0; i < _bytes.length; i += 1) { 1358 | buffer.put(_bytes[i], 8); 1359 | } 1360 | }; 1361 | 1362 | return _this; 1363 | }; 1364 | 1365 | //===================================================================== 1366 | // GIF Support etc. 1367 | // 1368 | 1369 | //--------------------------------------------------------------------- 1370 | // byteArrayOutputStream 1371 | //--------------------------------------------------------------------- 1372 | 1373 | var byteArrayOutputStream = function() { 1374 | 1375 | var _bytes = new Array(); 1376 | 1377 | var _this = {}; 1378 | 1379 | _this.writeByte = function(b) { 1380 | _bytes.push(b & 0xff); 1381 | }; 1382 | 1383 | _this.writeShort = function(i) { 1384 | _this.writeByte(i); 1385 | _this.writeByte(i >>> 8); 1386 | }; 1387 | 1388 | _this.writeBytes = function(b, off, len) { 1389 | off = off || 0; 1390 | len = len || b.length; 1391 | for (var i = 0; i < len; i += 1) { 1392 | _this.writeByte(b[i + off]); 1393 | } 1394 | }; 1395 | 1396 | _this.writeString = function(s) { 1397 | for (var i = 0; i < s.length; i += 1) { 1398 | _this.writeByte(s.charCodeAt(i) ); 1399 | } 1400 | }; 1401 | 1402 | _this.toByteArray = function() { 1403 | return _bytes; 1404 | }; 1405 | 1406 | _this.toString = function() { 1407 | var s = ''; 1408 | s += '['; 1409 | for (var i = 0; i < _bytes.length; i += 1) { 1410 | if (i > 0) { 1411 | s += ','; 1412 | } 1413 | s += _bytes[i]; 1414 | } 1415 | s += ']'; 1416 | return s; 1417 | }; 1418 | 1419 | return _this; 1420 | }; 1421 | 1422 | //--------------------------------------------------------------------- 1423 | // base64EncodeOutputStream 1424 | //--------------------------------------------------------------------- 1425 | 1426 | var base64EncodeOutputStream = function() { 1427 | 1428 | var _buffer = 0; 1429 | var _buflen = 0; 1430 | var _length = 0; 1431 | var _base64 = ''; 1432 | 1433 | var _this = {}; 1434 | 1435 | var writeEncoded = function(b) { 1436 | _base64 += String.fromCharCode(encode(b & 0x3f) ); 1437 | }; 1438 | 1439 | var encode = function(n) { 1440 | if (n < 0) { 1441 | // error. 1442 | } else if (n < 26) { 1443 | return 0x41 + n; 1444 | } else if (n < 52) { 1445 | return 0x61 + (n - 26); 1446 | } else if (n < 62) { 1447 | return 0x30 + (n - 52); 1448 | } else if (n == 62) { 1449 | return 0x2b; 1450 | } else if (n == 63) { 1451 | return 0x2f; 1452 | } 1453 | throw new Error('n:' + n); 1454 | }; 1455 | 1456 | _this.writeByte = function(n) { 1457 | 1458 | _buffer = (_buffer << 8) | (n & 0xff); 1459 | _buflen += 8; 1460 | _length += 1; 1461 | 1462 | while (_buflen >= 6) { 1463 | writeEncoded(_buffer >>> (_buflen - 6) ); 1464 | _buflen -= 6; 1465 | } 1466 | }; 1467 | 1468 | _this.flush = function() { 1469 | 1470 | if (_buflen > 0) { 1471 | writeEncoded(_buffer << (6 - _buflen) ); 1472 | _buffer = 0; 1473 | _buflen = 0; 1474 | } 1475 | 1476 | if (_length % 3 != 0) { 1477 | // padding 1478 | var padlen = 3 - _length % 3; 1479 | for (var i = 0; i < padlen; i += 1) { 1480 | _base64 += '='; 1481 | } 1482 | } 1483 | }; 1484 | 1485 | _this.toString = function() { 1486 | return _base64; 1487 | }; 1488 | 1489 | return _this; 1490 | }; 1491 | 1492 | //--------------------------------------------------------------------- 1493 | // base64DecodeInputStream 1494 | //--------------------------------------------------------------------- 1495 | 1496 | var base64DecodeInputStream = function(str) { 1497 | 1498 | var _str = str; 1499 | var _pos = 0; 1500 | var _buffer = 0; 1501 | var _buflen = 0; 1502 | 1503 | var _this = {}; 1504 | 1505 | _this.read = function() { 1506 | 1507 | while (_buflen < 8) { 1508 | 1509 | if (_pos >= _str.length) { 1510 | if (_buflen == 0) { 1511 | return -1; 1512 | } 1513 | throw new Error('unexpected end of file./' + _buflen); 1514 | } 1515 | 1516 | var c = _str.charAt(_pos); 1517 | _pos += 1; 1518 | 1519 | if (c == '=') { 1520 | _buflen = 0; 1521 | return -1; 1522 | } else if (c.match(/^\s$/) ) { 1523 | // ignore if whitespace. 1524 | continue; 1525 | } 1526 | 1527 | _buffer = (_buffer << 6) | decode(c.charCodeAt(0) ); 1528 | _buflen += 6; 1529 | } 1530 | 1531 | var n = (_buffer >>> (_buflen - 8) ) & 0xff; 1532 | _buflen -= 8; 1533 | return n; 1534 | }; 1535 | 1536 | var decode = function(c) { 1537 | if (0x41 <= c && c <= 0x5a) { 1538 | return c - 0x41; 1539 | } else if (0x61 <= c && c <= 0x7a) { 1540 | return c - 0x61 + 26; 1541 | } else if (0x30 <= c && c <= 0x39) { 1542 | return c - 0x30 + 52; 1543 | } else if (c == 0x2b) { 1544 | return 62; 1545 | } else if (c == 0x2f) { 1546 | return 63; 1547 | } else { 1548 | throw new Error('c:' + c); 1549 | } 1550 | }; 1551 | 1552 | return _this; 1553 | }; 1554 | 1555 | //--------------------------------------------------------------------- 1556 | // gifImage (B/W) 1557 | //--------------------------------------------------------------------- 1558 | 1559 | var gifImage = function(width, height) { 1560 | 1561 | var _width = width; 1562 | var _height = height; 1563 | var _data = new Array(width * height); 1564 | 1565 | var _this = {}; 1566 | 1567 | _this.setPixel = function(x, y, pixel) { 1568 | _data[y * _width + x] = pixel; 1569 | }; 1570 | 1571 | _this.write = function(out) { 1572 | 1573 | //--------------------------------- 1574 | // GIF Signature 1575 | 1576 | out.writeString('GIF87a'); 1577 | 1578 | //--------------------------------- 1579 | // Screen Descriptor 1580 | 1581 | out.writeShort(_width); 1582 | out.writeShort(_height); 1583 | 1584 | out.writeByte(0x80); // 2bit 1585 | out.writeByte(0); 1586 | out.writeByte(0); 1587 | 1588 | //--------------------------------- 1589 | // Global Color Map 1590 | 1591 | // black 1592 | out.writeByte(0x00); 1593 | out.writeByte(0x00); 1594 | out.writeByte(0x00); 1595 | 1596 | // white 1597 | out.writeByte(0xff); 1598 | out.writeByte(0xff); 1599 | out.writeByte(0xff); 1600 | 1601 | //--------------------------------- 1602 | // Image Descriptor 1603 | 1604 | out.writeString(','); 1605 | out.writeShort(0); 1606 | out.writeShort(0); 1607 | out.writeShort(_width); 1608 | out.writeShort(_height); 1609 | out.writeByte(0); 1610 | 1611 | //--------------------------------- 1612 | // Local Color Map 1613 | 1614 | //--------------------------------- 1615 | // Raster Data 1616 | 1617 | var lzwMinCodeSize = 2; 1618 | var raster = getLZWRaster(lzwMinCodeSize); 1619 | 1620 | out.writeByte(lzwMinCodeSize); 1621 | 1622 | var offset = 0; 1623 | 1624 | while (raster.length - offset > 255) { 1625 | out.writeByte(255); 1626 | out.writeBytes(raster, offset, 255); 1627 | offset += 255; 1628 | } 1629 | 1630 | out.writeByte(raster.length - offset); 1631 | out.writeBytes(raster, offset, raster.length - offset); 1632 | out.writeByte(0x00); 1633 | 1634 | //--------------------------------- 1635 | // GIF Terminator 1636 | out.writeString(';'); 1637 | }; 1638 | 1639 | var bitOutputStream = function(out) { 1640 | 1641 | var _out = out; 1642 | var _bitLength = 0; 1643 | var _bitBuffer = 0; 1644 | 1645 | var _this = {}; 1646 | 1647 | _this.write = function(data, length) { 1648 | 1649 | if ( (data >>> length) != 0) { 1650 | throw new Error('length over'); 1651 | } 1652 | 1653 | while (_bitLength + length >= 8) { 1654 | _out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) ); 1655 | length -= (8 - _bitLength); 1656 | data >>>= (8 - _bitLength); 1657 | _bitBuffer = 0; 1658 | _bitLength = 0; 1659 | } 1660 | 1661 | _bitBuffer = (data << _bitLength) | _bitBuffer; 1662 | _bitLength = _bitLength + length; 1663 | }; 1664 | 1665 | _this.flush = function() { 1666 | if (_bitLength > 0) { 1667 | _out.writeByte(_bitBuffer); 1668 | } 1669 | }; 1670 | 1671 | return _this; 1672 | }; 1673 | 1674 | var getLZWRaster = function(lzwMinCodeSize) { 1675 | 1676 | var clearCode = 1 << lzwMinCodeSize; 1677 | var endCode = (1 << lzwMinCodeSize) + 1; 1678 | var bitLength = lzwMinCodeSize + 1; 1679 | 1680 | // Setup LZWTable 1681 | var table = lzwTable(); 1682 | 1683 | for (var i = 0; i < clearCode; i += 1) { 1684 | table.add(String.fromCharCode(i) ); 1685 | } 1686 | table.add(String.fromCharCode(clearCode) ); 1687 | table.add(String.fromCharCode(endCode) ); 1688 | 1689 | var byteOut = byteArrayOutputStream(); 1690 | var bitOut = bitOutputStream(byteOut); 1691 | 1692 | // clear code 1693 | bitOut.write(clearCode, bitLength); 1694 | 1695 | var dataIndex = 0; 1696 | 1697 | var s = String.fromCharCode(_data[dataIndex]); 1698 | dataIndex += 1; 1699 | 1700 | while (dataIndex < _data.length) { 1701 | 1702 | var c = String.fromCharCode(_data[dataIndex]); 1703 | dataIndex += 1; 1704 | 1705 | if (table.contains(s + c) ) { 1706 | 1707 | s = s + c; 1708 | 1709 | } else { 1710 | 1711 | bitOut.write(table.indexOf(s), bitLength); 1712 | 1713 | if (table.size() < 0xfff) { 1714 | 1715 | if (table.size() == (1 << bitLength) ) { 1716 | bitLength += 1; 1717 | } 1718 | 1719 | table.add(s + c); 1720 | } 1721 | 1722 | s = c; 1723 | } 1724 | } 1725 | 1726 | bitOut.write(table.indexOf(s), bitLength); 1727 | 1728 | // end code 1729 | bitOut.write(endCode, bitLength); 1730 | 1731 | bitOut.flush(); 1732 | 1733 | return byteOut.toByteArray(); 1734 | }; 1735 | 1736 | var lzwTable = function() { 1737 | 1738 | var _map = {}; 1739 | var _size = 0; 1740 | 1741 | var _this = {}; 1742 | 1743 | _this.add = function(key) { 1744 | if (_this.contains(key) ) { 1745 | throw new Error('dup key:' + key); 1746 | } 1747 | _map[key] = _size; 1748 | _size += 1; 1749 | }; 1750 | 1751 | _this.size = function() { 1752 | return _size; 1753 | }; 1754 | 1755 | _this.indexOf = function(key) { 1756 | return _map[key]; 1757 | }; 1758 | 1759 | _this.contains = function(key) { 1760 | return typeof _map[key] != 'undefined'; 1761 | }; 1762 | 1763 | return _this; 1764 | }; 1765 | 1766 | return _this; 1767 | }; 1768 | 1769 | var createImgTag = function(width, height, getPixel, alt) { 1770 | 1771 | var gif = gifImage(width, height); 1772 | for (var y = 0; y < height; y += 1) { 1773 | for (var x = 0; x < width; x += 1) { 1774 | gif.setPixel(x, y, getPixel(x, y) ); 1775 | } 1776 | } 1777 | 1778 | var b = byteArrayOutputStream(); 1779 | gif.write(b); 1780 | 1781 | var base64 = base64EncodeOutputStream(); 1782 | var bytes = b.toByteArray(); 1783 | for (var i = 0; i < bytes.length; i += 1) { 1784 | base64.writeByte(bytes[i]); 1785 | } 1786 | base64.flush(); 1787 | 1788 | var img = ''; 1789 | img += '