├── .editorconfig ├── .gitattributes ├── .gitignore ├── .gitmodules ├── .jscsrc ├── .jshintrc ├── .travis.yml ├── CODEOWNERS ├── LICENSE ├── README.md ├── app.yaml ├── app ├── cache-polyfill.js ├── favicon.ico ├── fonts │ ├── RobotoCondensed-Bold.eot │ ├── RobotoCondensed-Bold.ttf │ ├── RobotoCondensed-Bold.woff │ ├── RobotoCondensed-BoldItalic.eot │ ├── RobotoCondensed-BoldItalic.ttf │ ├── RobotoCondensed-BoldItalic.woff │ ├── RobotoCondensed-Italic.eot │ ├── RobotoCondensed-Italic.ttf │ ├── RobotoCondensed-Italic.woff │ ├── RobotoCondensed-Light.eot │ ├── RobotoCondensed-Light.ttf │ ├── RobotoCondensed-Light.woff │ ├── RobotoCondensed-LightItalic.eot │ ├── RobotoCondensed-LightItalic.ttf │ ├── RobotoCondensed-LightItalic.woff │ ├── RobotoCondensed-Regular.eot │ ├── RobotoCondensed-Regular.ttf │ └── RobotoCondensed-Regular.woff ├── humans.txt ├── images │ ├── ic_camera_front_24px.svg │ ├── ic_camera_rear_24px.svg │ ├── icons │ │ ├── icons-hinted.ttf │ │ ├── icons.eot │ │ ├── icons.svg │ │ ├── icons.ttf │ │ ├── icons.woff │ │ └── icons.woff2 │ └── touch │ │ ├── apple-touch-icon.png │ │ ├── chrome-touch-icon-192x192.png │ │ ├── icon-128x128.png │ │ └── ms-touch-icon-144x144-precomposed.png ├── index.html ├── manifest.json ├── manifest.webapp ├── robots.txt ├── scripts │ ├── button │ │ └── button.js │ ├── jsqrcode │ │ ├── alignpat.js │ │ ├── bitmat.js │ │ ├── bmparser.js │ │ ├── datablock.js │ │ ├── databr.js │ │ ├── datamask.js │ │ ├── decoder.js │ │ ├── detector.js │ │ ├── errorlevel.js │ │ ├── findpat.js │ │ ├── formatinf.js │ │ ├── gf256.js │ │ ├── gf256poly.js │ │ ├── grid.js │ │ ├── qrcode.js │ │ ├── qrworker.js │ │ ├── rsdecoder.js │ │ └── version.js │ ├── layout │ │ └── layout.js │ ├── main.js │ ├── qrclient.js │ ├── resets │ │ └── _h5bp.scss │ ├── ripple │ │ └── ripple.js │ ├── third_party │ │ └── rAF.js │ └── wskComponentHandler.js ├── styles │ ├── _colors.scss │ ├── animation │ │ └── _animation.scss │ ├── button │ │ └── _button.scss │ ├── card │ │ └── _card.scss │ ├── layout │ │ └── _layout.scss │ ├── main.scss │ ├── palette │ │ └── _palette.scss │ ├── ripple │ │ └── _ripple.scss │ ├── shadow │ │ └── _shadow.scss │ └── typography │ │ └── _typography.scss └── sw.js ├── gulpfile.js ├── package.json ├── qrcode-essentials.zip └── qrsnapper.p12.enc /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | CONTRIBUTING.md export-ignore 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | .sass-cache 5 | .DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/.gitmodules -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "google", 3 | "validateLineBreaks": "LF", 4 | "validateIndentation": 2, 5 | "excludeFiles": ["node_modules/**"] 6 | } 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "newcap": true, 12 | "noarg": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": "vars", 16 | "strict": true, 17 | "globals": { 18 | "componentHandler": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | 5 | cache: 6 | directories: 7 | - vendor/bundle 8 | - node_modules 9 | 10 | before_install: 11 | - sudo pip install PyOpenSSL 12 | - openssl aes-256-cbc -K $encrypted_61236829dce9_key -iv $encrypted_61236829dce9_iv -in $PROJECT_ID.p12.enc -out $PROJECT_ID.p12 -d 13 | - curl https://sdk.cloud.google.com | bash 14 | - $HOME/google-cloud-sdk/bin/gcloud components update app -q 15 | - $HOME/google-cloud-sdk/bin/gcloud config set project $PROJECT_ID 16 | - $HOME/google-cloud-sdk/bin/gcloud auth activate-service-account $SRV_ACCOUNT --key-file $PROJECT_ID.p12 17 | - npm install --global gulp && npm install 18 | 19 | env: 20 | global: 21 | - CLOUDSDK_CORE_DISABLE_PROMPTS=1 22 | - CLOUDSDK_PYTHON_SITEPACKAGES=1 23 | - PROJECT_ID=qrsnapper 24 | - SRV_ACCOUNT=972466464352-3jqc7trki71pfa25atovoq6meh31fmve@developer.gserviceaccount.com 25 | 26 | after_success: 27 | - $HOME/google-cloud-sdk/bin/gcloud preview app deploy --version $TRAVIS_BRANCH ./app.yaml 28 | 29 | # branch whitelist 30 | branches: 31 | only: 32 | - production 33 | - slow 34 | 35 | # Work out the deployment to app engine. 36 | 37 | script: 38 | - gulp 39 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @udacity/active-public-content -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2015 Google Inc 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | 204 | All code in any directories or sub-directories that end with *.html or 205 | *.css is licensed under the Creative Commons Attribution International 206 | 4.0 License, which full text can be found here: 207 | https://creativecommons.org/licenses/by/4.0/legalcode. 208 | 209 | As an exception to this license, all html or css that is generated by 210 | the software at the direction of the user is copyright the user. The 211 | user has full ownership and control over such content, including 212 | whether and how they wish to license it. 213 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Archival Note 3 | This repository is deprecated; therefore, we are going to archive it. However, learners will be able to fork it to their personal Github account but cannot submit PRs to this repository. If you have any issues or suggestions to make, feel free to: 4 | - Utilize the https://knowledge.udacity.com/ forum to seek help on content-specific issues. 5 | - Submit a support ticket along with the link to your forked repository if (learners are) blocked for other reasons. Here are the links for the [retail consumers](https://udacity.zendesk.com/hc/en-us/requests/new) and [enterprise learners](https://udacityenterprise.zendesk.com/hc/en-us/requests/new?ticket_form_id=360000279131). -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: qrsnapper 2 | runtime: python27 3 | api_version: 1 4 | threadsafe: true 5 | 6 | default_expiration: "10s" 7 | 8 | handlers: 9 | - url: /manifest.json 10 | mime_type: application/json 11 | static_files: dist/manifest.json 12 | upload: dist/manifest.json 13 | secure: always 14 | http_headers: 15 | Access-Control-Allow-Origin: "*" 16 | 17 | - url: / 18 | mime_type: text/html 19 | static_files: dist/index.html 20 | secure: always 21 | upload: dist/index.html 22 | 23 | - url: /(.+) 24 | static_files: dist/\1 25 | upload: dist/(.+) 26 | secure: always 27 | http_headers: 28 | Access-Control-Allow-Origin: "*" 29 | 30 | skip_files: 31 | - ^.tmp/.* 32 | - ^app/.* 33 | - ^jsqrcode/.* 34 | - ^node_modules/.* -------------------------------------------------------------------------------- /app/cache-polyfill.js: -------------------------------------------------------------------------------- 1 | if (!Cache.prototype.add) { 2 | Cache.prototype.add = function add(request) { 3 | return this.addAll([request]); 4 | }; 5 | } 6 | 7 | if (!Cache.prototype.addAll) { 8 | Cache.prototype.addAll = function addAll(requests) { 9 | var cache = this; 10 | 11 | // Since DOMExceptions are not constructable: 12 | function NetworkError(message) { 13 | this.name = 'NetworkError'; 14 | this.code = 19; 15 | this.message = message; 16 | } 17 | NetworkError.prototype = Object.create(Error.prototype); 18 | 19 | return Promise.resolve().then(function() { 20 | if (arguments.length < 1) throw new TypeError(); 21 | 22 | // Simulate sequence<(Request or USVString)> binding: 23 | var sequence = []; 24 | 25 | requests = requests.map(function(request) { 26 | if (request instanceof Request) { 27 | return request; 28 | } 29 | else { 30 | return String(request); // may throw TypeError 31 | } 32 | }); 33 | 34 | return Promise.all( 35 | requests.map(function(request) { 36 | if (typeof request === 'string') { 37 | request = new Request(request); 38 | } 39 | 40 | var scheme = new URL(request.url).protocol; 41 | 42 | if (scheme !== 'http:' && scheme !== 'https:') { 43 | throw new NetworkError("Invalid scheme"); 44 | } 45 | 46 | return fetch(request.clone()); 47 | }) 48 | ); 49 | }).then(function(responses) { 50 | // TODO: check that requests don't overwrite one another 51 | // (don't think this is possible to polyfill due to opaque responses) 52 | return Promise.all( 53 | responses.map(function(response, i) { 54 | return cache.put(requests[i], response); 55 | }) 56 | ); 57 | }).then(function() { 58 | return undefined; 59 | }); 60 | }; 61 | } 62 | 63 | if (!CacheStorage.prototype.match) { 64 | // This is probably vulnerable to race conditions (removing caches etc) 65 | CacheStorage.prototype.match = function match(request, opts) { 66 | var caches = this; 67 | 68 | return this.keys().then(function(cacheNames) { 69 | var match; 70 | 71 | return cacheNames.reduce(function(chain, cacheName) { 72 | return chain.then(function() { 73 | return match || caches.open(cacheName).then(function(cache) { 74 | return cache.match(request, opts); 75 | }).then(function(response) { 76 | match = response; 77 | return match; 78 | }); 79 | }); 80 | }, Promise.resolve()); 81 | }); 82 | }; 83 | } -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/favicon.ico -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Bold.eot -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Bold.ttf -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Bold.woff -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-BoldItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-BoldItalic.eot -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-BoldItalic.ttf -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-BoldItalic.woff -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Italic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Italic.eot -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Italic.ttf -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Italic.woff -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Light.eot -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Light.ttf -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Light.woff -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-LightItalic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-LightItalic.eot -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-LightItalic.ttf -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-LightItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-LightItalic.woff -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Regular.eot -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Regular.ttf -------------------------------------------------------------------------------- /app/fonts/RobotoCondensed-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/fonts/RobotoCondensed-Regular.woff -------------------------------------------------------------------------------- /app/humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | Paul Kinlan 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | HTML5, CSS3, GetUserMedia, Web Starter Kit 15 | -------------------------------------------------------------------------------- /app/images/ic_camera_front_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/images/ic_camera_rear_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/images/icons/icons-hinted.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/icons/icons-hinted.ttf -------------------------------------------------------------------------------- /app/images/icons/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/icons/icons.eot -------------------------------------------------------------------------------- /app/images/icons/icons.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/images/icons/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/icons/icons.ttf -------------------------------------------------------------------------------- /app/images/icons/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/icons/icons.woff -------------------------------------------------------------------------------- /app/images/icons/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/icons/icons.woff2 -------------------------------------------------------------------------------- /app/images/touch/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/touch/apple-touch-icon.png -------------------------------------------------------------------------------- /app/images/touch/chrome-touch-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/touch/chrome-touch-icon-192x192.png -------------------------------------------------------------------------------- /app/images/touch/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/touch/icon-128x128.png -------------------------------------------------------------------------------- /app/images/touch/ms-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/app/images/touch/ms-touch-icon-144x144-precomposed.png -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Capture QR Code 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 | 38 |
39 | 40 |
Point at a QR code
41 |
42 | 43 | 47 |
48 |
49 | 50 |
51 |
52 | QR Code found 53 |
54 |
55 |
56 |
57 |
58 | 60 |
61 |
62 |
63 |
64 |
65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 100 | 101 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Capture QR Code", 3 | "short_name": "Capture QR Code", 4 | "icons": [{ 5 | "src": "images/touch/icon-128x128.png", 6 | "sizes": "128x128" 7 | }, { 8 | "src": "images/touch/apple-touch-icon.png", 9 | "sizes": "152x152" 10 | }, { 11 | "src": "images/touch/ms-touch-icon-144x144-precomposed.png", 12 | "sizes": "144x144" 13 | }, { 14 | "src": "images/touch/chrome-touch-icon-192x192.png", 15 | "sizes": "192x192" 16 | }], 17 | "start_url": "/index.html?homescreen=1", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /app/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "name": "Capture QR Code", 4 | "launch_path": "/index.html", 5 | "description": "QR Code Capture tool", 6 | "icons": { 7 | "128": "/images/touch/icon-128x128.png" 8 | }, 9 | "developer": { 10 | "name": "Paul Kinlan", 11 | "url": "http://paul.kinlan.me" 12 | }, 13 | "installs_allowed_from": [ 14 | "*" 15 | ], 16 | "default_locale": "en", 17 | "permissions": { 18 | }, 19 | "locales": { 20 | "en": { 21 | "name": "Capture QR Code", 22 | "description": "QR Code Capture tool" 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | 3 | # Allow crawling of all content 4 | User-agent: * 5 | Disallow: 6 | -------------------------------------------------------------------------------- /app/scripts/button/button.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Class constructor for Button WSK component. 3 | * Implements WSK component design pattern defined at: 4 | * https://github.com/jasonmayes/wsk-component-design-pattern 5 | * @param {HTMLElement} element The element that will be upgraded. 6 | */ 7 | function MaterialButton(element) { 8 | 'use strict'; 9 | 10 | this.element_ = element; 11 | 12 | // Initialize instance. 13 | this.init(); 14 | } 15 | 16 | /** 17 | * Store constants in one place so they can be updated easily. 18 | * @enum {string | number} 19 | * @private 20 | */ 21 | MaterialButton.prototype.Constant_ = { 22 | // None for now. 23 | }; 24 | 25 | /** 26 | * Store strings for class names defined by this component that are used in 27 | * JavaScript. This allows us to simply change it in one place should we 28 | * decide to modify at a later date. 29 | * @enum {string} 30 | * @private 31 | */ 32 | MaterialButton.prototype.CssClasses_ = { 33 | WSK_JS_RIPPLE_EFFECT: 'wsk-js-ripple-effect', 34 | 35 | WSK_BUTTON_RIPPLE_CONTAINER: 'wsk-button__ripple-container', 36 | 37 | WSK_RIPPLE: 'wsk-ripple' 38 | }; 39 | 40 | 41 | /** 42 | * Handle blur of element. 43 | * @param {HTMLElement} element The instance of a button we want to blur. 44 | * @private 45 | */ 46 | MaterialButton.prototype.blurHandlerGenerator_ = function(element) { 47 | 'use strict'; 48 | 49 | return function() {element.blur();}; 50 | }; 51 | 52 | 53 | /** 54 | * Initialize element. 55 | */ 56 | MaterialButton.prototype.init = function() { 57 | 'use strict'; 58 | 59 | if (this.element_) { 60 | var blurHandler = this.blurHandlerGenerator_(this.element_); 61 | if (this.element_.classList.contains( 62 | this.CssClasses_.WSK_JS_RIPPLE_EFFECT)) { 63 | var rippleContainer = document.createElement('span'); 64 | rippleContainer.classList.add( 65 | this.CssClasses_.WSK_BUTTON_RIPPLE_CONTAINER); 66 | var ripple = document.createElement('span'); 67 | ripple.classList.add(this.CssClasses_.WSK_RIPPLE); 68 | rippleContainer.appendChild(ripple); 69 | ripple.addEventListener('mouseup', blurHandler); 70 | this.element_.appendChild(rippleContainer); 71 | } 72 | this.element_.addEventListener('mouseup', blurHandler); 73 | } 74 | }; 75 | 76 | 77 | // The component registers itself. It can assume componentHandler is available 78 | // in the global scope. 79 | componentHandler.register({ 80 | constructor: MaterialButton, 81 | classAsString: 'MaterialButton', 82 | cssClass: 'wsk-js-button' 83 | }); 84 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/alignpat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function AlignmentPattern(posX, posY, estimatedModuleSize) 27 | { 28 | this.x=posX; 29 | this.y=posY; 30 | this.count = 1; 31 | this.estimatedModuleSize = estimatedModuleSize; 32 | 33 | this.__defineGetter__("EstimatedModuleSize", function() 34 | { 35 | return this.estimatedModuleSize; 36 | }); 37 | this.__defineGetter__("Count", function() 38 | { 39 | return this.count; 40 | }); 41 | this.__defineGetter__("X", function() 42 | { 43 | return Math.floor(this.x); 44 | }); 45 | this.__defineGetter__("Y", function() 46 | { 47 | return Math.floor(this.y); 48 | }); 49 | this.incrementCount = function() 50 | { 51 | this.count++; 52 | } 53 | this.aboutEquals=function( moduleSize, i, j) 54 | { 55 | if (Math.abs(i - this.y) <= moduleSize && Math.abs(j - this.x) <= moduleSize) 56 | { 57 | var moduleSizeDiff = Math.abs(moduleSize - this.estimatedModuleSize); 58 | return moduleSizeDiff <= 1.0 || moduleSizeDiff / this.estimatedModuleSize <= 1.0; 59 | } 60 | return false; 61 | } 62 | 63 | } 64 | 65 | function AlignmentPatternFinder( image, startX, startY, width, height, moduleSize, resultPointCallback) 66 | { 67 | this.image = image; 68 | this.possibleCenters = new Array(); 69 | this.startX = startX; 70 | this.startY = startY; 71 | this.width = width; 72 | this.height = height; 73 | this.moduleSize = moduleSize; 74 | this.crossCheckStateCount = new Array(0,0,0); 75 | this.resultPointCallback = resultPointCallback; 76 | 77 | this.centerFromEnd=function(stateCount, end) 78 | { 79 | return (end - stateCount[2]) - stateCount[1] / 2.0; 80 | } 81 | this.foundPatternCross = function(stateCount) 82 | { 83 | var moduleSize = this.moduleSize; 84 | var maxVariance = moduleSize / 2.0; 85 | for (var i = 0; i < 3; i++) 86 | { 87 | if (Math.abs(moduleSize - stateCount[i]) >= maxVariance) 88 | { 89 | return false; 90 | } 91 | } 92 | return true; 93 | } 94 | 95 | this.crossCheckVertical=function( startI, centerJ, maxCount, originalStateCountTotal) 96 | { 97 | var image = this.image; 98 | 99 | var maxI = qrcode.height; 100 | var stateCount = this.crossCheckStateCount; 101 | stateCount[0] = 0; 102 | stateCount[1] = 0; 103 | stateCount[2] = 0; 104 | 105 | // Start counting up from center 106 | var i = startI; 107 | while (i >= 0 && image[centerJ + i*qrcode.width] && stateCount[1] <= maxCount) 108 | { 109 | stateCount[1]++; 110 | i--; 111 | } 112 | // If already too many modules in this state or ran off the edge: 113 | if (i < 0 || stateCount[1] > maxCount) 114 | { 115 | return NaN; 116 | } 117 | while (i >= 0 && !image[centerJ + i*qrcode.width] && stateCount[0] <= maxCount) 118 | { 119 | stateCount[0]++; 120 | i--; 121 | } 122 | if (stateCount[0] > maxCount) 123 | { 124 | return NaN; 125 | } 126 | 127 | // Now also count down from center 128 | i = startI + 1; 129 | while (i < maxI && image[centerJ + i*qrcode.width] && stateCount[1] <= maxCount) 130 | { 131 | stateCount[1]++; 132 | i++; 133 | } 134 | if (i == maxI || stateCount[1] > maxCount) 135 | { 136 | return NaN; 137 | } 138 | while (i < maxI && !image[centerJ + i*qrcode.width] && stateCount[2] <= maxCount) 139 | { 140 | stateCount[2]++; 141 | i++; 142 | } 143 | if (stateCount[2] > maxCount) 144 | { 145 | return NaN; 146 | } 147 | 148 | var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; 149 | if (5 * Math.abs(stateCountTotal - originalStateCountTotal) >= 2 * originalStateCountTotal) 150 | { 151 | return NaN; 152 | } 153 | 154 | return this.foundPatternCross(stateCount)?this.centerFromEnd(stateCount, i):NaN; 155 | } 156 | 157 | this.handlePossibleCenter=function( stateCount, i, j) 158 | { 159 | var stateCountTotal = stateCount[0] + stateCount[1] + stateCount[2]; 160 | var centerJ = this.centerFromEnd(stateCount, j); 161 | var centerI = this.crossCheckVertical(i, Math.floor (centerJ), 2 * stateCount[1], stateCountTotal); 162 | if (!isNaN(centerI)) 163 | { 164 | var estimatedModuleSize = (stateCount[0] + stateCount[1] + stateCount[2]) / 3.0; 165 | var max = this.possibleCenters.length; 166 | for (var index = 0; index < max; index++) 167 | { 168 | var center = this.possibleCenters[index]; 169 | // Look for about the same center and module size: 170 | if (center.aboutEquals(estimatedModuleSize, centerI, centerJ)) 171 | { 172 | return new AlignmentPattern(centerJ, centerI, estimatedModuleSize); 173 | } 174 | } 175 | // Hadn't found this before; save it 176 | var point = new AlignmentPattern(centerJ, centerI, estimatedModuleSize); 177 | this.possibleCenters.push(point); 178 | if (this.resultPointCallback != null) 179 | { 180 | this.resultPointCallback.foundPossibleResultPoint(point); 181 | } 182 | } 183 | return null; 184 | } 185 | 186 | this.find = function() 187 | { 188 | var startX = this.startX; 189 | var height = this.height; 190 | var maxJ = startX + width; 191 | var middleI = startY + (height >> 1); 192 | // We are looking for black/white/black modules in 1:1:1 ratio; 193 | // this tracks the number of black/white/black modules seen so far 194 | var stateCount = new Array(0,0,0); 195 | for (var iGen = 0; iGen < height; iGen++) 196 | { 197 | // Search from middle outwards 198 | var i = middleI + ((iGen & 0x01) == 0?((iGen + 1) >> 1):- ((iGen + 1) >> 1)); 199 | stateCount[0] = 0; 200 | stateCount[1] = 0; 201 | stateCount[2] = 0; 202 | var j = startX; 203 | // Burn off leading white pixels before anything else; if we start in the middle of 204 | // a white run, it doesn't make sense to count its length, since we don't know if the 205 | // white run continued to the left of the start point 206 | while (j < maxJ && !image[j + qrcode.width* i]) 207 | { 208 | j++; 209 | } 210 | var currentState = 0; 211 | while (j < maxJ) 212 | { 213 | if (image[j + i*qrcode.width]) 214 | { 215 | // Black pixel 216 | if (currentState == 1) 217 | { 218 | // Counting black pixels 219 | stateCount[currentState]++; 220 | } 221 | else 222 | { 223 | // Counting white pixels 224 | if (currentState == 2) 225 | { 226 | // A winner? 227 | if (this.foundPatternCross(stateCount)) 228 | { 229 | // Yes 230 | var confirmed = this.handlePossibleCenter(stateCount, i, j); 231 | if (confirmed != null) 232 | { 233 | return confirmed; 234 | } 235 | } 236 | stateCount[0] = stateCount[2]; 237 | stateCount[1] = 1; 238 | stateCount[2] = 0; 239 | currentState = 1; 240 | } 241 | else 242 | { 243 | stateCount[++currentState]++; 244 | } 245 | } 246 | } 247 | else 248 | { 249 | // White pixel 250 | if (currentState == 1) 251 | { 252 | // Counting black pixels 253 | currentState++; 254 | } 255 | stateCount[currentState]++; 256 | } 257 | j++; 258 | } 259 | if (this.foundPatternCross(stateCount)) 260 | { 261 | var confirmed = this.handlePossibleCenter(stateCount, i, maxJ); 262 | if (confirmed != null) 263 | { 264 | return confirmed; 265 | } 266 | } 267 | } 268 | 269 | // Hmm, nothing we saw was observed and confirmed twice. If we had 270 | // any guess at all, return it. 271 | if (!(this.possibleCenters.length == 0)) 272 | { 273 | return this.possibleCenters[0]; 274 | } 275 | 276 | throw "Couldn't find enough alignment patterns"; 277 | } 278 | 279 | } -------------------------------------------------------------------------------- /app/scripts/jsqrcode/bitmat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function BitMatrix( width, height) 27 | { 28 | if(!height) 29 | height=width; 30 | if (width < 1 || height < 1) 31 | { 32 | throw "Both dimensions must be greater than 0"; 33 | } 34 | this.width = width; 35 | this.height = height; 36 | var rowSize = width >> 5; 37 | if ((width & 0x1f) != 0) 38 | { 39 | rowSize++; 40 | } 41 | this.rowSize = rowSize; 42 | this.bits = new Array(rowSize * height); 43 | for(var i=0;i> 5); 66 | return ((URShift(this.bits[offset], (x & 0x1f))) & 1) != 0; 67 | } 68 | this.set_Renamed=function( x, y) 69 | { 70 | var offset = y * this.rowSize + (x >> 5); 71 | this.bits[offset] |= 1 << (x & 0x1f); 72 | } 73 | this.flip=function( x, y) 74 | { 75 | var offset = y * this.rowSize + (x >> 5); 76 | this.bits[offset] ^= 1 << (x & 0x1f); 77 | } 78 | this.clear=function() 79 | { 80 | var max = this.bits.length; 81 | for (var i = 0; i < max; i++) 82 | { 83 | this.bits[i] = 0; 84 | } 85 | } 86 | this.setRegion=function( left, top, width, height) 87 | { 88 | if (top < 0 || left < 0) 89 | { 90 | throw "Left and top must be nonnegative"; 91 | } 92 | if (height < 1 || width < 1) 93 | { 94 | throw "Height and width must be at least 1"; 95 | } 96 | var right = left + width; 97 | var bottom = top + height; 98 | if (bottom > this.height || right > this.width) 99 | { 100 | throw "The region must fit inside the matrix"; 101 | } 102 | for (var y = top; y < bottom; y++) 103 | { 104 | var offset = y * this.rowSize; 105 | for (var x = left; x < right; x++) 106 | { 107 | this.bits[offset + (x >> 5)] |= 1 << (x & 0x1f); 108 | } 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /app/scripts/jsqrcode/bmparser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function BitMatrixParser(bitMatrix) 27 | { 28 | var dimension = bitMatrix.Dimension; 29 | if (dimension < 21 || (dimension & 0x03) != 1) 30 | { 31 | throw "Error BitMatrixParser"; 32 | } 33 | this.bitMatrix = bitMatrix; 34 | this.parsedVersion = null; 35 | this.parsedFormatInfo = null; 36 | 37 | this.copyBit=function( i, j, versionBits) 38 | { 39 | return this.bitMatrix.get_Renamed(i, j)?(versionBits << 1) | 0x1:versionBits << 1; 40 | } 41 | 42 | this.readFormatInformation=function() 43 | { 44 | if (this.parsedFormatInfo != null) 45 | { 46 | return this.parsedFormatInfo; 47 | } 48 | 49 | // Read top-left format info bits 50 | var formatInfoBits = 0; 51 | for (var i = 0; i < 6; i++) 52 | { 53 | formatInfoBits = this.copyBit(i, 8, formatInfoBits); 54 | } 55 | // .. and skip a bit in the timing pattern ... 56 | formatInfoBits = this.copyBit(7, 8, formatInfoBits); 57 | formatInfoBits = this.copyBit(8, 8, formatInfoBits); 58 | formatInfoBits = this.copyBit(8, 7, formatInfoBits); 59 | // .. and skip a bit in the timing pattern ... 60 | for (var j = 5; j >= 0; j--) 61 | { 62 | formatInfoBits = this.copyBit(8, j, formatInfoBits); 63 | } 64 | 65 | this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); 66 | if (this.parsedFormatInfo != null) 67 | { 68 | return this.parsedFormatInfo; 69 | } 70 | 71 | // Hmm, failed. Try the top-right/bottom-left pattern 72 | var dimension = this.bitMatrix.Dimension; 73 | formatInfoBits = 0; 74 | var iMin = dimension - 8; 75 | for (var i = dimension - 1; i >= iMin; i--) 76 | { 77 | formatInfoBits = this.copyBit(i, 8, formatInfoBits); 78 | } 79 | for (var j = dimension - 7; j < dimension; j++) 80 | { 81 | formatInfoBits = this.copyBit(8, j, formatInfoBits); 82 | } 83 | 84 | this.parsedFormatInfo = FormatInformation.decodeFormatInformation(formatInfoBits); 85 | if (this.parsedFormatInfo != null) 86 | { 87 | return this.parsedFormatInfo; 88 | } 89 | throw "Error readFormatInformation"; 90 | } 91 | this.readVersion=function() 92 | { 93 | 94 | if (this.parsedVersion != null) 95 | { 96 | return this.parsedVersion; 97 | } 98 | 99 | var dimension = this.bitMatrix.Dimension; 100 | 101 | var provisionalVersion = (dimension - 17) >> 2; 102 | if (provisionalVersion <= 6) 103 | { 104 | return Version.getVersionForNumber(provisionalVersion); 105 | } 106 | 107 | // Read top-right version info: 3 wide by 6 tall 108 | var versionBits = 0; 109 | var ijMin = dimension - 11; 110 | for (var j = 5; j >= 0; j--) 111 | { 112 | for (var i = dimension - 9; i >= ijMin; i--) 113 | { 114 | versionBits = this.copyBit(i, j, versionBits); 115 | } 116 | } 117 | 118 | this.parsedVersion = Version.decodeVersionInformation(versionBits); 119 | if (this.parsedVersion != null && this.parsedVersion.DimensionForVersion == dimension) 120 | { 121 | return this.parsedVersion; 122 | } 123 | 124 | // Hmm, failed. Try bottom left: 6 wide by 3 tall 125 | versionBits = 0; 126 | for (var i = 5; i >= 0; i--) 127 | { 128 | for (var j = dimension - 9; j >= ijMin; j--) 129 | { 130 | versionBits = this.copyBit(i, j, versionBits); 131 | } 132 | } 133 | 134 | this.parsedVersion = Version.decodeVersionInformation(versionBits); 135 | if (this.parsedVersion != null && this.parsedVersion.DimensionForVersion == dimension) 136 | { 137 | return this.parsedVersion; 138 | } 139 | throw "Error readVersion"; 140 | } 141 | this.readCodewords=function() 142 | { 143 | 144 | var formatInfo = this.readFormatInformation(); 145 | var version = this.readVersion(); 146 | 147 | // Get the data mask for the format used in this QR Code. This will exclude 148 | // some bits from reading as we wind through the bit matrix. 149 | var dataMask = DataMask.forReference( formatInfo.DataMask); 150 | var dimension = this.bitMatrix.Dimension; 151 | dataMask.unmaskBitMatrix(this.bitMatrix, dimension); 152 | 153 | var functionPattern = version.buildFunctionPattern(); 154 | 155 | var readingUp = true; 156 | var result = new Array(version.TotalCodewords); 157 | var resultOffset = 0; 158 | var currentByte = 0; 159 | var bitsRead = 0; 160 | // Read columns in pairs, from right to left 161 | for (var j = dimension - 1; j > 0; j -= 2) 162 | { 163 | if (j == 6) 164 | { 165 | // Skip whole column with vertical alignment pattern; 166 | // saves time and makes the other code proceed more cleanly 167 | j--; 168 | } 169 | // Read alternatingly from bottom to top then top to bottom 170 | for (var count = 0; count < dimension; count++) 171 | { 172 | var i = readingUp?dimension - 1 - count:count; 173 | for (var col = 0; col < 2; col++) 174 | { 175 | // Ignore bits covered by the function pattern 176 | if (!functionPattern.get_Renamed(j - col, i)) 177 | { 178 | // Read a bit 179 | bitsRead++; 180 | currentByte <<= 1; 181 | if (this.bitMatrix.get_Renamed(j - col, i)) 182 | { 183 | currentByte |= 1; 184 | } 185 | // If we've made a whole byte, save it off 186 | if (bitsRead == 8) 187 | { 188 | result[resultOffset++] = currentByte; 189 | bitsRead = 0; 190 | currentByte = 0; 191 | } 192 | } 193 | } 194 | } 195 | readingUp ^= true; // readingUp = !readingUp; // switch directions 196 | } 197 | if (resultOffset != version.TotalCodewords) 198 | { 199 | throw "Error readCodewords"; 200 | } 201 | return result; 202 | } 203 | } -------------------------------------------------------------------------------- /app/scripts/jsqrcode/datablock.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function DataBlock(numDataCodewords, codewords) 27 | { 28 | this.numDataCodewords = numDataCodewords; 29 | this.codewords = codewords; 30 | 31 | this.__defineGetter__("NumDataCodewords", function() 32 | { 33 | return this.numDataCodewords; 34 | }); 35 | this.__defineGetter__("Codewords", function() 36 | { 37 | return this.codewords; 38 | }); 39 | } 40 | 41 | DataBlock.getDataBlocks=function(rawCodewords, version, ecLevel) 42 | { 43 | 44 | if (rawCodewords.length != version.TotalCodewords) 45 | { 46 | throw "ArgumentException"; 47 | } 48 | 49 | // Figure out the number and size of data blocks used by this version and 50 | // error correction level 51 | var ecBlocks = version.getECBlocksForLevel(ecLevel); 52 | 53 | // First count the total number of data blocks 54 | var totalBlocks = 0; 55 | var ecBlockArray = ecBlocks.getECBlocks(); 56 | for (var i = 0; i < ecBlockArray.length; i++) 57 | { 58 | totalBlocks += ecBlockArray[i].Count; 59 | } 60 | 61 | // Now establish DataBlocks of the appropriate size and number of data codewords 62 | var result = new Array(totalBlocks); 63 | var numResultBlocks = 0; 64 | for (var j = 0; j < ecBlockArray.length; j++) 65 | { 66 | var ecBlock = ecBlockArray[j]; 67 | for (var i = 0; i < ecBlock.Count; i++) 68 | { 69 | var numDataCodewords = ecBlock.DataCodewords; 70 | var numBlockCodewords = ecBlocks.ECCodewordsPerBlock + numDataCodewords; 71 | result[numResultBlocks++] = new DataBlock(numDataCodewords, new Array(numBlockCodewords)); 72 | } 73 | } 74 | 75 | // All blocks have the same amount of data, except that the last n 76 | // (where n may be 0) have 1 more byte. Figure out where these start. 77 | var shorterBlocksTotalCodewords = result[0].codewords.length; 78 | var longerBlocksStartAt = result.length - 1; 79 | while (longerBlocksStartAt >= 0) 80 | { 81 | var numCodewords = result[longerBlocksStartAt].codewords.length; 82 | if (numCodewords == shorterBlocksTotalCodewords) 83 | { 84 | break; 85 | } 86 | longerBlocksStartAt--; 87 | } 88 | longerBlocksStartAt++; 89 | 90 | var shorterBlocksNumDataCodewords = shorterBlocksTotalCodewords - ecBlocks.ECCodewordsPerBlock; 91 | // The last elements of result may be 1 element longer; 92 | // first fill out as many elements as all of them have 93 | var rawCodewordsOffset = 0; 94 | for (var i = 0; i < shorterBlocksNumDataCodewords; i++) 95 | { 96 | for (var j = 0; j < numResultBlocks; j++) 97 | { 98 | result[j].codewords[i] = rawCodewords[rawCodewordsOffset++]; 99 | } 100 | } 101 | // Fill out the last data block in the longer ones 102 | for (var j = longerBlocksStartAt; j < numResultBlocks; j++) 103 | { 104 | result[j].codewords[shorterBlocksNumDataCodewords] = rawCodewords[rawCodewordsOffset++]; 105 | } 106 | // Now add in error correction blocks 107 | var max = result[0].codewords.length; 108 | for (var i = shorterBlocksNumDataCodewords; i < max; i++) 109 | { 110 | for (var j = 0; j < numResultBlocks; j++) 111 | { 112 | var iOffset = j < longerBlocksStartAt?i:i + 1; 113 | result[j].codewords[iOffset] = rawCodewords[rawCodewordsOffset++]; 114 | } 115 | } 116 | return result; 117 | } 118 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/databr.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function QRCodeDataBlockReader(blocks, version, numErrorCorrectionCode) 27 | { 28 | this.blockPointer = 0; 29 | this.bitPointer = 7; 30 | this.dataLength = 0; 31 | this.blocks = blocks; 32 | this.numErrorCorrectionCode = numErrorCorrectionCode; 33 | if (version <= 9) 34 | this.dataLengthMode = 0; 35 | else if (version >= 10 && version <= 26) 36 | this.dataLengthMode = 1; 37 | else if (version >= 27 && version <= 40) 38 | this.dataLengthMode = 2; 39 | 40 | this.getNextBits = function( numBits) 41 | { 42 | var bits = 0; 43 | if (numBits < this.bitPointer + 1) 44 | { 45 | // next word fits into current data block 46 | var mask = 0; 47 | for (var i = 0; i < numBits; i++) 48 | { 49 | mask += (1 << i); 50 | } 51 | mask <<= (this.bitPointer - numBits + 1); 52 | 53 | bits = (this.blocks[this.blockPointer] & mask) >> (this.bitPointer - numBits + 1); 54 | this.bitPointer -= numBits; 55 | return bits; 56 | } 57 | else if (numBits < this.bitPointer + 1 + 8) 58 | { 59 | // next word crosses 2 data blocks 60 | var mask1 = 0; 61 | for (var i = 0; i < this.bitPointer + 1; i++) 62 | { 63 | mask1 += (1 << i); 64 | } 65 | bits = (this.blocks[this.blockPointer] & mask1) << (numBits - (this.bitPointer + 1)); 66 | this.blockPointer++; 67 | bits += ((this.blocks[this.blockPointer]) >> (8 - (numBits - (this.bitPointer + 1)))); 68 | 69 | this.bitPointer = this.bitPointer - numBits % 8; 70 | if (this.bitPointer < 0) 71 | { 72 | this.bitPointer = 8 + this.bitPointer; 73 | } 74 | return bits; 75 | } 76 | else if (numBits < this.bitPointer + 1 + 16) 77 | { 78 | // next word crosses 3 data blocks 79 | var mask1 = 0; // mask of first block 80 | var mask3 = 0; // mask of 3rd block 81 | //bitPointer + 1 : number of bits of the 1st block 82 | //8 : number of the 2nd block (note that use already 8bits because next word uses 3 data blocks) 83 | //numBits - (bitPointer + 1 + 8) : number of bits of the 3rd block 84 | for (var i = 0; i < this.bitPointer + 1; i++) 85 | { 86 | mask1 += (1 << i); 87 | } 88 | var bitsFirstBlock = (this.blocks[this.blockPointer] & mask1) << (numBits - (this.bitPointer + 1)); 89 | this.blockPointer++; 90 | 91 | var bitsSecondBlock = this.blocks[this.blockPointer] << (numBits - (this.bitPointer + 1 + 8)); 92 | this.blockPointer++; 93 | 94 | for (var i = 0; i < numBits - (this.bitPointer + 1 + 8); i++) 95 | { 96 | mask3 += (1 << i); 97 | } 98 | mask3 <<= 8 - (numBits - (this.bitPointer + 1 + 8)); 99 | var bitsThirdBlock = (this.blocks[this.blockPointer] & mask3) >> (8 - (numBits - (this.bitPointer + 1 + 8))); 100 | 101 | bits = bitsFirstBlock + bitsSecondBlock + bitsThirdBlock; 102 | this.bitPointer = this.bitPointer - (numBits - 8) % 8; 103 | if (this.bitPointer < 0) 104 | { 105 | this.bitPointer = 8 + this.bitPointer; 106 | } 107 | return bits; 108 | } 109 | else 110 | { 111 | return 0; 112 | } 113 | } 114 | this.NextMode=function() 115 | { 116 | if ((this.blockPointer > this.blocks.length - this.numErrorCorrectionCode - 2)) 117 | return 0; 118 | else 119 | return this.getNextBits(4); 120 | } 121 | this.getDataLength=function( modeIndicator) 122 | { 123 | var index = 0; 124 | while (true) 125 | { 126 | if ((modeIndicator >> index) == 1) 127 | break; 128 | index++; 129 | } 130 | 131 | return this.getNextBits(qrcode.sizeOfDataLengthInfo[this.dataLengthMode][index]); 132 | } 133 | this.getRomanAndFigureString=function( dataLength) 134 | { 135 | var length = dataLength; 136 | var intData = 0; 137 | var strData = ""; 138 | var tableRomanAndFigure = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'); 139 | do 140 | { 141 | if (length > 1) 142 | { 143 | intData = this.getNextBits(11); 144 | var firstLetter = Math.floor(intData / 45); 145 | var secondLetter = intData % 45; 146 | strData += tableRomanAndFigure[firstLetter]; 147 | strData += tableRomanAndFigure[secondLetter]; 148 | length -= 2; 149 | } 150 | else if (length == 1) 151 | { 152 | intData = this.getNextBits(6); 153 | strData += tableRomanAndFigure[intData]; 154 | length -= 1; 155 | } 156 | } 157 | while (length > 0); 158 | 159 | return strData; 160 | } 161 | this.getFigureString=function( dataLength) 162 | { 163 | var length = dataLength; 164 | var intData = 0; 165 | var strData = ""; 166 | do 167 | { 168 | if (length >= 3) 169 | { 170 | intData = this.getNextBits(10); 171 | if (intData < 100) 172 | strData += "0"; 173 | if (intData < 10) 174 | strData += "0"; 175 | length -= 3; 176 | } 177 | else if (length == 2) 178 | { 179 | intData = this.getNextBits(7); 180 | if (intData < 10) 181 | strData += "0"; 182 | length -= 2; 183 | } 184 | else if (length == 1) 185 | { 186 | intData = this.getNextBits(4); 187 | length -= 1; 188 | } 189 | strData += intData; 190 | } 191 | while (length > 0); 192 | 193 | return strData; 194 | } 195 | this.get8bitByteArray=function( dataLength) 196 | { 197 | var length = dataLength; 198 | var intData = 0; 199 | var output = new Array(); 200 | 201 | do 202 | { 203 | intData = this.getNextBits(8); 204 | output.push( intData); 205 | length--; 206 | } 207 | while (length > 0); 208 | return output; 209 | } 210 | this.getKanjiString=function( dataLength) 211 | { 212 | var length = dataLength; 213 | var intData = 0; 214 | var unicodeString = ""; 215 | do 216 | { 217 | intData = getNextBits(13); 218 | var lowerByte = intData % 0xC0; 219 | var higherByte = intData / 0xC0; 220 | 221 | var tempWord = (higherByte << 8) + lowerByte; 222 | var shiftjisWord = 0; 223 | if (tempWord + 0x8140 <= 0x9FFC) 224 | { 225 | // between 8140 - 9FFC on Shift_JIS character set 226 | shiftjisWord = tempWord + 0x8140; 227 | } 228 | else 229 | { 230 | // between E040 - EBBF on Shift_JIS character set 231 | shiftjisWord = tempWord + 0xC140; 232 | } 233 | 234 | //var tempByte = new Array(0,0); 235 | //tempByte[0] = (sbyte) (shiftjisWord >> 8); 236 | //tempByte[1] = (sbyte) (shiftjisWord & 0xFF); 237 | //unicodeString += new String(SystemUtils.ToCharArray(SystemUtils.ToByteArray(tempByte))); 238 | unicodeString += String.fromCharCode(shiftjisWord); 239 | length--; 240 | } 241 | while (length > 0); 242 | 243 | 244 | return unicodeString; 245 | } 246 | 247 | this.__defineGetter__("DataByte", function() 248 | { 249 | var output = new Array(); 250 | var MODE_NUMBER = 1; 251 | var MODE_ROMAN_AND_NUMBER = 2; 252 | var MODE_8BIT_BYTE = 4; 253 | var MODE_KANJI = 8; 254 | do 255 | { 256 | var mode = this.NextMode(); 257 | //canvas.println("mode: " + mode); 258 | if (mode == 0) 259 | { 260 | if (output.length > 0) 261 | break; 262 | else 263 | throw "Empty data block"; 264 | } 265 | //if (mode != 1 && mode != 2 && mode != 4 && mode != 8) 266 | // break; 267 | //} 268 | if (mode != MODE_NUMBER && mode != MODE_ROMAN_AND_NUMBER && mode != MODE_8BIT_BYTE && mode != MODE_KANJI) 269 | { 270 | /* canvas.println("Invalid mode: " + mode); 271 | mode = guessMode(mode); 272 | canvas.println("Guessed mode: " + mode); */ 273 | throw "Invalid mode: " + mode + " in (block:" + this.blockPointer + " bit:" + this.bitPointer + ")"; 274 | } 275 | dataLength = this.getDataLength(mode); 276 | if (dataLength < 1) 277 | throw "Invalid data length: " + dataLength; 278 | //canvas.println("length: " + dataLength); 279 | switch (mode) 280 | { 281 | 282 | case MODE_NUMBER: 283 | //canvas.println("Mode: Figure"); 284 | var temp_str = this.getFigureString(dataLength); 285 | var ta = new Array(temp_str.length); 286 | for(var j=0;j 7) 31 | { 32 | throw "System.ArgumentException"; 33 | } 34 | return DataMask.DATA_MASKS[reference]; 35 | } 36 | 37 | function DataMask000() 38 | { 39 | this.unmaskBitMatrix=function(bits, dimension) 40 | { 41 | for (var i = 0; i < dimension; i++) 42 | { 43 | for (var j = 0; j < dimension; j++) 44 | { 45 | if (this.isMasked(i, j)) 46 | { 47 | bits.flip(j, i); 48 | } 49 | } 50 | } 51 | } 52 | this.isMasked=function( i, j) 53 | { 54 | return ((i + j) & 0x01) == 0; 55 | } 56 | } 57 | 58 | function DataMask001() 59 | { 60 | this.unmaskBitMatrix=function(bits, dimension) 61 | { 62 | for (var i = 0; i < dimension; i++) 63 | { 64 | for (var j = 0; j < dimension; j++) 65 | { 66 | if (this.isMasked(i, j)) 67 | { 68 | bits.flip(j, i); 69 | } 70 | } 71 | } 72 | } 73 | this.isMasked=function( i, j) 74 | { 75 | return (i & 0x01) == 0; 76 | } 77 | } 78 | 79 | function DataMask010() 80 | { 81 | this.unmaskBitMatrix=function(bits, dimension) 82 | { 83 | for (var i = 0; i < dimension; i++) 84 | { 85 | for (var j = 0; j < dimension; j++) 86 | { 87 | if (this.isMasked(i, j)) 88 | { 89 | bits.flip(j, i); 90 | } 91 | } 92 | } 93 | } 94 | this.isMasked=function( i, j) 95 | { 96 | return j % 3 == 0; 97 | } 98 | } 99 | 100 | function DataMask011() 101 | { 102 | this.unmaskBitMatrix=function(bits, dimension) 103 | { 104 | for (var i = 0; i < dimension; i++) 105 | { 106 | for (var j = 0; j < dimension; j++) 107 | { 108 | if (this.isMasked(i, j)) 109 | { 110 | bits.flip(j, i); 111 | } 112 | } 113 | } 114 | } 115 | this.isMasked=function( i, j) 116 | { 117 | return (i + j) % 3 == 0; 118 | } 119 | } 120 | 121 | function DataMask100() 122 | { 123 | this.unmaskBitMatrix=function(bits, dimension) 124 | { 125 | for (var i = 0; i < dimension; i++) 126 | { 127 | for (var j = 0; j < dimension; j++) 128 | { 129 | if (this.isMasked(i, j)) 130 | { 131 | bits.flip(j, i); 132 | } 133 | } 134 | } 135 | } 136 | this.isMasked=function( i, j) 137 | { 138 | return (((URShift(i, 1)) + (j / 3)) & 0x01) == 0; 139 | } 140 | } 141 | 142 | function DataMask101() 143 | { 144 | this.unmaskBitMatrix=function(bits, dimension) 145 | { 146 | for (var i = 0; i < dimension; i++) 147 | { 148 | for (var j = 0; j < dimension; j++) 149 | { 150 | if (this.isMasked(i, j)) 151 | { 152 | bits.flip(j, i); 153 | } 154 | } 155 | } 156 | } 157 | this.isMasked=function( i, j) 158 | { 159 | var temp = i * j; 160 | return (temp & 0x01) + (temp % 3) == 0; 161 | } 162 | } 163 | 164 | function DataMask110() 165 | { 166 | this.unmaskBitMatrix=function(bits, dimension) 167 | { 168 | for (var i = 0; i < dimension; i++) 169 | { 170 | for (var j = 0; j < dimension; j++) 171 | { 172 | if (this.isMasked(i, j)) 173 | { 174 | bits.flip(j, i); 175 | } 176 | } 177 | } 178 | } 179 | this.isMasked=function( i, j) 180 | { 181 | var temp = i * j; 182 | return (((temp & 0x01) + (temp % 3)) & 0x01) == 0; 183 | } 184 | } 185 | function DataMask111() 186 | { 187 | this.unmaskBitMatrix=function(bits, dimension) 188 | { 189 | for (var i = 0; i < dimension; i++) 190 | { 191 | for (var j = 0; j < dimension; j++) 192 | { 193 | if (this.isMasked(i, j)) 194 | { 195 | bits.flip(j, i); 196 | } 197 | } 198 | } 199 | } 200 | this.isMasked=function( i, j) 201 | { 202 | return ((((i + j) & 0x01) + ((i * j) % 3)) & 0x01) == 0; 203 | } 204 | } 205 | 206 | DataMask.DATA_MASKS = new Array(new DataMask000(), new DataMask001(), new DataMask010(), new DataMask011(), new DataMask100(), new DataMask101(), new DataMask110(), new DataMask111()); 207 | 208 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/decoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | Decoder={}; 27 | Decoder.rsDecoder = new ReedSolomonDecoder(GF256.QR_CODE_FIELD); 28 | 29 | Decoder.correctErrors=function( codewordBytes, numDataCodewords) 30 | { 31 | var numCodewords = codewordBytes.length; 32 | // First read into an array of ints 33 | var codewordsInts = new Array(numCodewords); 34 | for (var i = 0; i < numCodewords; i++) 35 | { 36 | codewordsInts[i] = codewordBytes[i] & 0xFF; 37 | } 38 | var numECCodewords = codewordBytes.length - numDataCodewords; 39 | try 40 | { 41 | Decoder.rsDecoder.decode(codewordsInts, numECCodewords); 42 | //var corrector = new ReedSolomon(codewordsInts, numECCodewords); 43 | //corrector.correct(); 44 | } 45 | catch ( rse) 46 | { 47 | throw rse; 48 | } 49 | // Copy back into array of bytes -- only need to worry about the bytes that were data 50 | // We don't care about errors in the error-correction codewords 51 | for (var i = 0; i < numDataCodewords; i++) 52 | { 53 | codewordBytes[i] = codewordsInts[i]; 54 | } 55 | } 56 | 57 | Decoder.decode=function(bits) 58 | { 59 | var parser = new BitMatrixParser(bits); 60 | var version = parser.readVersion(); 61 | var ecLevel = parser.readFormatInformation().ErrorCorrectionLevel; 62 | 63 | // Read codewords 64 | var codewords = parser.readCodewords(); 65 | 66 | // Separate into data blocks 67 | var dataBlocks = DataBlock.getDataBlocks(codewords, version, ecLevel); 68 | 69 | // Count total number of data bytes 70 | var totalBytes = 0; 71 | for (var i = 0; i < dataBlocks.length; i++) 72 | { 73 | totalBytes += dataBlocks[i].NumDataCodewords; 74 | } 75 | var resultBytes = new Array(totalBytes); 76 | var resultOffset = 0; 77 | 78 | // Error-correct and copy data blocks together into a stream of bytes 79 | for (var j = 0; j < dataBlocks.length; j++) 80 | { 81 | var dataBlock = dataBlocks[j]; 82 | var codewordBytes = dataBlock.Codewords; 83 | var numDataCodewords = dataBlock.NumDataCodewords; 84 | Decoder.correctErrors(codewordBytes, numDataCodewords); 85 | for (var i = 0; i < numDataCodewords; i++) 86 | { 87 | resultBytes[resultOffset++] = codewordBytes[i]; 88 | } 89 | } 90 | 91 | // Decode the contents of that stream of bytes 92 | var reader = new QRCodeDataBlockReader(resultBytes, version.VersionNumber, ecLevel.Bits); 93 | return reader; 94 | //return DecodedBitStreamParser.decode(resultBytes, version, ecLevel); 95 | } 96 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/detector.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function PerspectiveTransform( a11, a21, a31, a12, a22, a32, a13, a23, a33) 27 | { 28 | this.a11 = a11; 29 | this.a12 = a12; 30 | this.a13 = a13; 31 | this.a21 = a21; 32 | this.a22 = a22; 33 | this.a23 = a23; 34 | this.a31 = a31; 35 | this.a32 = a32; 36 | this.a33 = a33; 37 | this.transformPoints1=function( points) 38 | { 39 | var max = points.length; 40 | var a11 = this.a11; 41 | var a12 = this.a12; 42 | var a13 = this.a13; 43 | var a21 = this.a21; 44 | var a22 = this.a22; 45 | var a23 = this.a23; 46 | var a31 = this.a31; 47 | var a32 = this.a32; 48 | var a33 = this.a33; 49 | for (var i = 0; i < max; i += 2) 50 | { 51 | var x = points[i]; 52 | var y = points[i + 1]; 53 | var denominator = a13 * x + a23 * y + a33; 54 | points[i] = (a11 * x + a21 * y + a31) / denominator; 55 | points[i + 1] = (a12 * x + a22 * y + a32) / denominator; 56 | } 57 | } 58 | this. transformPoints2=function(xValues, yValues) 59 | { 60 | var n = xValues.length; 61 | for (var i = 0; i < n; i++) 62 | { 63 | var x = xValues[i]; 64 | var y = yValues[i]; 65 | var denominator = this.a13 * x + this.a23 * y + this.a33; 66 | xValues[i] = (this.a11 * x + this.a21 * y + this.a31) / denominator; 67 | yValues[i] = (this.a12 * x + this.a22 * y + this.a32) / denominator; 68 | } 69 | } 70 | 71 | this.buildAdjoint=function() 72 | { 73 | // Adjoint is the transpose of the cofactor matrix: 74 | return new PerspectiveTransform(this.a22 * this.a33 - this.a23 * this.a32, this.a23 * this.a31 - this.a21 * this.a33, this.a21 * this.a32 - this.a22 * this.a31, this.a13 * this.a32 - this.a12 * this.a33, this.a11 * this.a33 - this.a13 * this.a31, this.a12 * this.a31 - this.a11 * this.a32, this.a12 * this.a23 - this.a13 * this.a22, this.a13 * this.a21 - this.a11 * this.a23, this.a11 * this.a22 - this.a12 * this.a21); 75 | } 76 | this.times=function( other) 77 | { 78 | return new PerspectiveTransform(this.a11 * other.a11 + this.a21 * other.a12 + this.a31 * other.a13, this.a11 * other.a21 + this.a21 * other.a22 + this.a31 * other.a23, this.a11 * other.a31 + this.a21 * other.a32 + this.a31 * other.a33, this.a12 * other.a11 + this.a22 * other.a12 + this.a32 * other.a13, this.a12 * other.a21 + this.a22 * other.a22 + this.a32 * other.a23, this.a12 * other.a31 + this.a22 * other.a32 + this.a32 * other.a33, this.a13 * other.a11 + this.a23 * other.a12 +this.a33 * other.a13, this.a13 * other.a21 + this.a23 * other.a22 + this.a33 * other.a23, this.a13 * other.a31 + this.a23 * other.a32 + this.a33 * other.a33); 79 | } 80 | 81 | } 82 | 83 | PerspectiveTransform.quadrilateralToQuadrilateral=function( x0, y0, x1, y1, x2, y2, x3, y3, x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p) 84 | { 85 | 86 | var qToS = this.quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3); 87 | var sToQ = this.squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p); 88 | return sToQ.times(qToS); 89 | } 90 | 91 | PerspectiveTransform.squareToQuadrilateral=function( x0, y0, x1, y1, x2, y2, x3, y3) 92 | { 93 | dy2 = y3 - y2; 94 | dy3 = y0 - y1 + y2 - y3; 95 | if (dy2 == 0.0 && dy3 == 0.0) 96 | { 97 | return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0, 0.0, 1.0); 98 | } 99 | else 100 | { 101 | dx1 = x1 - x2; 102 | dx2 = x3 - x2; 103 | dx3 = x0 - x1 + x2 - x3; 104 | dy1 = y1 - y2; 105 | denominator = dx1 * dy2 - dx2 * dy1; 106 | a13 = (dx3 * dy2 - dx2 * dy3) / denominator; 107 | a23 = (dx1 * dy3 - dx3 * dy1) / denominator; 108 | return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0); 109 | } 110 | } 111 | 112 | PerspectiveTransform.quadrilateralToSquare=function( x0, y0, x1, y1, x2, y2, x3, y3) 113 | { 114 | // Here, the adjoint serves as the inverse: 115 | return this.squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint(); 116 | } 117 | 118 | function DetectorResult(bits, points) 119 | { 120 | this.bits = bits; 121 | this.points = points; 122 | } 123 | 124 | 125 | function Detector(image) 126 | { 127 | this.image=image; 128 | this.resultPointCallback = null; 129 | 130 | this.sizeOfBlackWhiteBlackRun=function( fromX, fromY, toX, toY) 131 | { 132 | // Mild variant of Bresenham's algorithm; 133 | // see http://en.wikipedia.org/wiki/Bresenham's_line_algorithm 134 | var steep = Math.abs(toY - fromY) > Math.abs(toX - fromX); 135 | if (steep) 136 | { 137 | var temp = fromX; 138 | fromX = fromY; 139 | fromY = temp; 140 | temp = toX; 141 | toX = toY; 142 | toY = temp; 143 | } 144 | 145 | var dx = Math.abs(toX - fromX); 146 | var dy = Math.abs(toY - fromY); 147 | var error = - dx >> 1; 148 | var ystep = fromY < toY?1:- 1; 149 | var xstep = fromX < toX?1:- 1; 150 | var state = 0; // In black pixels, looking for white, first or second time 151 | for (var x = fromX, y = fromY; x != toX; x += xstep) 152 | { 153 | 154 | var realX = steep?y:x; 155 | var realY = steep?x:y; 156 | if (state == 1) 157 | { 158 | // In white pixels, looking for black 159 | if (this.image[realX + realY*qrcode.width]) 160 | { 161 | state++; 162 | } 163 | } 164 | else 165 | { 166 | if (!this.image[realX + realY*qrcode.width]) 167 | { 168 | state++; 169 | } 170 | } 171 | 172 | if (state == 3) 173 | { 174 | // Found black, white, black, and stumbled back onto white; done 175 | var diffX = x - fromX; 176 | var diffY = y - fromY; 177 | return Math.sqrt( (diffX * diffX + diffY * diffY)); 178 | } 179 | error += dy; 180 | if (error > 0) 181 | { 182 | if (y == toY) 183 | { 184 | break; 185 | } 186 | y += ystep; 187 | error -= dx; 188 | } 189 | } 190 | var diffX2 = toX - fromX; 191 | var diffY2 = toY - fromY; 192 | return Math.sqrt( (diffX2 * diffX2 + diffY2 * diffY2)); 193 | } 194 | 195 | 196 | this.sizeOfBlackWhiteBlackRunBothWays=function( fromX, fromY, toX, toY) 197 | { 198 | 199 | var result = this.sizeOfBlackWhiteBlackRun(fromX, fromY, toX, toY); 200 | 201 | // Now count other way -- don't run off image though of course 202 | var scale = 1.0; 203 | var otherToX = fromX - (toX - fromX); 204 | if (otherToX < 0) 205 | { 206 | scale = fromX / (fromX - otherToX); 207 | otherToX = 0; 208 | } 209 | else if (otherToX >= qrcode.width) 210 | { 211 | scale = (qrcode.width - 1 - fromX) / (otherToX - fromX); 212 | otherToX = qrcode.width - 1; 213 | } 214 | var otherToY = Math.floor (fromY - (toY - fromY) * scale); 215 | 216 | scale = 1.0; 217 | if (otherToY < 0) 218 | { 219 | scale = fromY / (fromY - otherToY); 220 | otherToY = 0; 221 | } 222 | else if (otherToY >= qrcode.height) 223 | { 224 | scale = (qrcode.height - 1 - fromY) / (otherToY - fromY); 225 | otherToY = qrcode.height - 1; 226 | } 227 | otherToX = Math.floor (fromX + (otherToX - fromX) * scale); 228 | 229 | result += this.sizeOfBlackWhiteBlackRun(fromX, fromY, otherToX, otherToY); 230 | return result - 1.0; // -1 because we counted the middle pixel twice 231 | } 232 | 233 | 234 | 235 | this.calculateModuleSizeOneWay=function( pattern, otherPattern) 236 | { 237 | var moduleSizeEst1 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor( pattern.X), Math.floor( pattern.Y), Math.floor( otherPattern.X), Math.floor(otherPattern.Y)); 238 | var moduleSizeEst2 = this.sizeOfBlackWhiteBlackRunBothWays(Math.floor(otherPattern.X), Math.floor(otherPattern.Y), Math.floor( pattern.X), Math.floor(pattern.Y)); 239 | if (isNaN(moduleSizeEst1)) 240 | { 241 | return moduleSizeEst2 / 7.0; 242 | } 243 | if (isNaN(moduleSizeEst2)) 244 | { 245 | return moduleSizeEst1 / 7.0; 246 | } 247 | // Average them, and divide by 7 since we've counted the width of 3 black modules, 248 | // and 1 white and 1 black module on either side. Ergo, divide sum by 14. 249 | return (moduleSizeEst1 + moduleSizeEst2) / 14.0; 250 | } 251 | 252 | 253 | this.calculateModuleSize=function( topLeft, topRight, bottomLeft) 254 | { 255 | // Take the average 256 | return (this.calculateModuleSizeOneWay(topLeft, topRight) + this.calculateModuleSizeOneWay(topLeft, bottomLeft)) / 2.0; 257 | } 258 | 259 | this.distance=function( pattern1, pattern2) 260 | { 261 | xDiff = pattern1.X - pattern2.X; 262 | yDiff = pattern1.Y - pattern2.Y; 263 | return Math.sqrt( (xDiff * xDiff + yDiff * yDiff)); 264 | } 265 | this.computeDimension=function( topLeft, topRight, bottomLeft, moduleSize) 266 | { 267 | 268 | var tltrCentersDimension = Math.round(this.distance(topLeft, topRight) / moduleSize); 269 | var tlblCentersDimension = Math.round(this.distance(topLeft, bottomLeft) / moduleSize); 270 | var dimension = ((tltrCentersDimension + tlblCentersDimension) >> 1) + 7; 271 | switch (dimension & 0x03) 272 | { 273 | 274 | // mod 4 275 | case 0: 276 | dimension++; 277 | break; 278 | // 1? do nothing 279 | 280 | case 2: 281 | dimension--; 282 | break; 283 | 284 | case 3: 285 | throw "Error"; 286 | } 287 | return dimension; 288 | } 289 | 290 | this.findAlignmentInRegion=function( overallEstModuleSize, estAlignmentX, estAlignmentY, allowanceFactor) 291 | { 292 | // Look for an alignment pattern (3 modules in size) around where it 293 | // should be 294 | var allowance = Math.floor (allowanceFactor * overallEstModuleSize); 295 | var alignmentAreaLeftX = Math.max(0, estAlignmentX - allowance); 296 | var alignmentAreaRightX = Math.min(qrcode.width - 1, estAlignmentX + allowance); 297 | if (alignmentAreaRightX - alignmentAreaLeftX < overallEstModuleSize * 3) 298 | { 299 | throw "Error"; 300 | } 301 | 302 | var alignmentAreaTopY = Math.max(0, estAlignmentY - allowance); 303 | var alignmentAreaBottomY = Math.min(qrcode.height - 1, estAlignmentY + allowance); 304 | 305 | var alignmentFinder = new AlignmentPatternFinder(this.image, alignmentAreaLeftX, alignmentAreaTopY, alignmentAreaRightX - alignmentAreaLeftX, alignmentAreaBottomY - alignmentAreaTopY, overallEstModuleSize, this.resultPointCallback); 306 | return alignmentFinder.find(); 307 | } 308 | 309 | this.createTransform=function( topLeft, topRight, bottomLeft, alignmentPattern, dimension) 310 | { 311 | var dimMinusThree = dimension - 3.5; 312 | var bottomRightX; 313 | var bottomRightY; 314 | var sourceBottomRightX; 315 | var sourceBottomRightY; 316 | if (alignmentPattern != null) 317 | { 318 | bottomRightX = alignmentPattern.X; 319 | bottomRightY = alignmentPattern.Y; 320 | sourceBottomRightX = sourceBottomRightY = dimMinusThree - 3.0; 321 | } 322 | else 323 | { 324 | // Don't have an alignment pattern, just make up the bottom-right point 325 | bottomRightX = (topRight.X - topLeft.X) + bottomLeft.X; 326 | bottomRightY = (topRight.Y - topLeft.Y) + bottomLeft.Y; 327 | sourceBottomRightX = sourceBottomRightY = dimMinusThree; 328 | } 329 | 330 | var transform = PerspectiveTransform.quadrilateralToQuadrilateral(3.5, 3.5, dimMinusThree, 3.5, sourceBottomRightX, sourceBottomRightY, 3.5, dimMinusThree, topLeft.X, topLeft.Y, topRight.X, topRight.Y, bottomRightX, bottomRightY, bottomLeft.X, bottomLeft.Y); 331 | 332 | return transform; 333 | } 334 | 335 | this.sampleGrid=function( image, transform, dimension) 336 | { 337 | 338 | var sampler = GridSampler; 339 | return sampler.sampleGrid3(image, dimension, transform); 340 | } 341 | 342 | this.processFinderPatternInfo = function( info) 343 | { 344 | 345 | var topLeft = info.TopLeft; 346 | var topRight = info.TopRight; 347 | var bottomLeft = info.BottomLeft; 348 | 349 | var moduleSize = this.calculateModuleSize(topLeft, topRight, bottomLeft); 350 | if (moduleSize < 1.0) 351 | { 352 | throw "Error"; 353 | } 354 | var dimension = this.computeDimension(topLeft, topRight, bottomLeft, moduleSize); 355 | var provisionalVersion = Version.getProvisionalVersionForDimension(dimension); 356 | var modulesBetweenFPCenters = provisionalVersion.DimensionForVersion - 7; 357 | 358 | var alignmentPattern = null; 359 | // Anything above version 1 has an alignment pattern 360 | if (provisionalVersion.AlignmentPatternCenters.length > 0) 361 | { 362 | 363 | // Guess where a "bottom right" finder pattern would have been 364 | var bottomRightX = topRight.X - topLeft.X + bottomLeft.X; 365 | var bottomRightY = topRight.Y - topLeft.Y + bottomLeft.Y; 366 | 367 | // Estimate that alignment pattern is closer by 3 modules 368 | // from "bottom right" to known top left location 369 | var correctionToTopLeft = 1.0 - 3.0 / modulesBetweenFPCenters; 370 | var estAlignmentX = Math.floor (topLeft.X + correctionToTopLeft * (bottomRightX - topLeft.X)); 371 | var estAlignmentY = Math.floor (topLeft.Y + correctionToTopLeft * (bottomRightY - topLeft.Y)); 372 | 373 | // Kind of arbitrary -- expand search radius before giving up 374 | for (var i = 4; i <= 16; i <<= 1) 375 | { 376 | //try 377 | //{ 378 | alignmentPattern = this.findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, i); 379 | break; 380 | //} 381 | //catch (re) 382 | //{ 383 | // try next round 384 | //} 385 | } 386 | // If we didn't find alignment pattern... well try anyway without it 387 | } 388 | 389 | var transform = this.createTransform(topLeft, topRight, bottomLeft, alignmentPattern, dimension); 390 | 391 | var bits = this.sampleGrid(this.image, transform, dimension); 392 | 393 | var points; 394 | if (alignmentPattern == null) 395 | { 396 | points = new Array(bottomLeft, topLeft, topRight); 397 | } 398 | else 399 | { 400 | points = new Array(bottomLeft, topLeft, topRight, alignmentPattern); 401 | } 402 | return new DetectorResult(bits, points); 403 | } 404 | 405 | 406 | 407 | this.detect=function() 408 | { 409 | var info = new FinderPatternFinder().findFinderPattern(this.image); 410 | 411 | return this.processFinderPatternInfo(info); 412 | } 413 | } -------------------------------------------------------------------------------- /app/scripts/jsqrcode/errorlevel.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function ErrorCorrectionLevel(ordinal, bits, name) 27 | { 28 | this.ordinal_Renamed_Field = ordinal; 29 | this.bits = bits; 30 | this.name = name; 31 | this.__defineGetter__("Bits", function() 32 | { 33 | return this.bits; 34 | }); 35 | this.__defineGetter__("Name", function() 36 | { 37 | return this.name; 38 | }); 39 | this.ordinal=function() 40 | { 41 | return this.ordinal_Renamed_Field; 42 | } 43 | } 44 | 45 | ErrorCorrectionLevel.forBits=function( bits) 46 | { 47 | if (bits < 0 || bits >= FOR_BITS.length) 48 | { 49 | throw "ArgumentException"; 50 | } 51 | return FOR_BITS[bits]; 52 | } 53 | 54 | var L = new ErrorCorrectionLevel(0, 0x01, "L"); 55 | var M = new ErrorCorrectionLevel(1, 0x00, "M"); 56 | var Q = new ErrorCorrectionLevel(2, 0x03, "Q"); 57 | var H = new ErrorCorrectionLevel(3, 0x02, "H"); 58 | var FOR_BITS = new Array( M, L, H, Q); 59 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/formatinf.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | var FORMAT_INFO_MASK_QR = 0x5412; 27 | var FORMAT_INFO_DECODE_LOOKUP = new Array(new Array(0x5412, 0x00), new Array(0x5125, 0x01), new Array(0x5E7C, 0x02), new Array(0x5B4B, 0x03), new Array(0x45F9, 0x04), new Array(0x40CE, 0x05), new Array(0x4F97, 0x06), new Array(0x4AA0, 0x07), new Array(0x77C4, 0x08), new Array(0x72F3, 0x09), new Array(0x7DAA, 0x0A), new Array(0x789D, 0x0B), new Array(0x662F, 0x0C), new Array(0x6318, 0x0D), new Array(0x6C41, 0x0E), new Array(0x6976, 0x0F), new Array(0x1689, 0x10), new Array(0x13BE, 0x11), new Array(0x1CE7, 0x12), new Array(0x19D0, 0x13), new Array(0x0762, 0x14), new Array(0x0255, 0x15), new Array(0x0D0C, 0x16), new Array(0x083B, 0x17), new Array(0x355F, 0x18), new Array(0x3068, 0x19), new Array(0x3F31, 0x1A), new Array(0x3A06, 0x1B), new Array(0x24B4, 0x1C), new Array(0x2183, 0x1D), new Array(0x2EDA, 0x1E), new Array(0x2BED, 0x1F)); 28 | var BITS_SET_IN_HALF_BYTE = new Array(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4); 29 | 30 | 31 | function FormatInformation(formatInfo) 32 | { 33 | this.errorCorrectionLevel = ErrorCorrectionLevel.forBits((formatInfo >> 3) & 0x03); 34 | this.dataMask = (formatInfo & 0x07); 35 | 36 | this.__defineGetter__("ErrorCorrectionLevel", function() 37 | { 38 | return this.errorCorrectionLevel; 39 | }); 40 | this.__defineGetter__("DataMask", function() 41 | { 42 | return this.dataMask; 43 | }); 44 | this.GetHashCode=function() 45 | { 46 | return (this.errorCorrectionLevel.ordinal() << 3) | dataMask; 47 | } 48 | this.Equals=function( o) 49 | { 50 | var other = o; 51 | return this.errorCorrectionLevel == other.errorCorrectionLevel && this.dataMask == other.dataMask; 52 | } 53 | } 54 | 55 | FormatInformation.numBitsDiffering=function( a, b) 56 | { 57 | a ^= b; // a now has a 1 bit exactly where its bit differs with b's 58 | // Count bits set quickly with a series of lookups: 59 | return BITS_SET_IN_HALF_BYTE[a & 0x0F] + BITS_SET_IN_HALF_BYTE[(URShift(a, 4) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 8) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 12) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 16) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 20) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 24) & 0x0F)] + BITS_SET_IN_HALF_BYTE[(URShift(a, 28) & 0x0F)]; 60 | } 61 | 62 | FormatInformation.decodeFormatInformation=function( maskedFormatInfo) 63 | { 64 | var formatInfo = FormatInformation.doDecodeFormatInformation(maskedFormatInfo); 65 | if (formatInfo != null) 66 | { 67 | return formatInfo; 68 | } 69 | // Should return null, but, some QR codes apparently 70 | // do not mask this info. Try again by actually masking the pattern 71 | // first 72 | return FormatInformation.doDecodeFormatInformation(maskedFormatInfo ^ FORMAT_INFO_MASK_QR); 73 | } 74 | FormatInformation.doDecodeFormatInformation=function( maskedFormatInfo) 75 | { 76 | // Find the int in FORMAT_INFO_DECODE_LOOKUP with fewest bits differing 77 | var bestDifference = 0xffffffff; 78 | var bestFormatInfo = 0; 79 | for (var i = 0; i < FORMAT_INFO_DECODE_LOOKUP.length; i++) 80 | { 81 | var decodeInfo = FORMAT_INFO_DECODE_LOOKUP[i]; 82 | var targetInfo = decodeInfo[0]; 83 | if (targetInfo == maskedFormatInfo) 84 | { 85 | // Found an exact match 86 | return new FormatInformation(decodeInfo[1]); 87 | } 88 | var bitsDifference = this.numBitsDiffering(maskedFormatInfo, targetInfo); 89 | if (bitsDifference < bestDifference) 90 | { 91 | bestFormatInfo = decodeInfo[1]; 92 | bestDifference = bitsDifference; 93 | } 94 | } 95 | // Hamming distance of the 32 masked codes is 7, by construction, so <= 3 bits 96 | // differing means we found a match 97 | if (bestDifference <= 3) 98 | { 99 | return new FormatInformation(bestFormatInfo); 100 | } 101 | return null; 102 | } 103 | 104 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/gf256.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function GF256( primitive) 27 | { 28 | this.expTable = new Array(256); 29 | this.logTable = new Array(256); 30 | var x = 1; 31 | for (var i = 0; i < 256; i++) 32 | { 33 | this.expTable[i] = x; 34 | x <<= 1; // x = x * 2; we're assuming the generator alpha is 2 35 | if (x >= 0x100) 36 | { 37 | x ^= primitive; 38 | } 39 | } 40 | for (var i = 0; i < 255; i++) 41 | { 42 | this.logTable[this.expTable[i]] = i; 43 | } 44 | // logTable[0] == 0 but this should never be used 45 | var at0=new Array(1);at0[0]=0; 46 | this.zero = new GF256Poly(this, new Array(at0)); 47 | var at1=new Array(1);at1[0]=1; 48 | this.one = new GF256Poly(this, new Array(at1)); 49 | 50 | this.__defineGetter__("Zero", function() 51 | { 52 | return this.zero; 53 | }); 54 | this.__defineGetter__("One", function() 55 | { 56 | return this.one; 57 | }); 58 | this.buildMonomial=function( degree, coefficient) 59 | { 60 | if (degree < 0) 61 | { 62 | throw "System.ArgumentException"; 63 | } 64 | if (coefficient == 0) 65 | { 66 | return zero; 67 | } 68 | var coefficients = new Array(degree + 1); 69 | for(var i=0;i 1 && coefficients[0] == 0) 35 | { 36 | // Leading term must be non-zero for anything except the constant polynomial "0" 37 | var firstNonZero = 1; 38 | while (firstNonZero < coefficientsLength && coefficients[firstNonZero] == 0) 39 | { 40 | firstNonZero++; 41 | } 42 | if (firstNonZero == coefficientsLength) 43 | { 44 | this.coefficients = field.Zero.coefficients; 45 | } 46 | else 47 | { 48 | this.coefficients = new Array(coefficientsLength - firstNonZero); 49 | for(var i=0;i largerCoefficients.length) 121 | { 122 | var temp = smallerCoefficients; 123 | smallerCoefficients = largerCoefficients; 124 | largerCoefficients = temp; 125 | } 126 | var sumDiff = new Array(largerCoefficients.length); 127 | var lengthDiff = largerCoefficients.length - smallerCoefficients.length; 128 | // Copy high-order terms only found in higher-degree polynomial's coefficients 129 | //Array.Copy(largerCoefficients, 0, sumDiff, 0, lengthDiff); 130 | for(var ci=0;ci= other.Degree && !remainder.Zero) 219 | { 220 | var degreeDifference = remainder.Degree - other.Degree; 221 | var scale = this.field.multiply(remainder.getCoefficient(remainder.Degree), inverseDenominatorLeadingTerm); 222 | var term = other.multiplyByMonomial(degreeDifference, scale); 223 | var iterationQuotient = this.field.buildMonomial(degreeDifference, scale); 224 | quotient = quotient.addOrSubtract(iterationQuotient); 225 | remainder = remainder.addOrSubtract(term); 226 | } 227 | 228 | return new Array(quotient, remainder); 229 | } 230 | } -------------------------------------------------------------------------------- /app/scripts/jsqrcode/grid.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | GridSampler = {}; 27 | 28 | GridSampler.checkAndNudgePoints=function( image, points) 29 | { 30 | var width = qrcode.width; 31 | var height = qrcode.height; 32 | // Check and nudge points from start until we see some that are OK: 33 | var nudged = true; 34 | for (var offset = 0; offset < points.length && nudged; offset += 2) 35 | { 36 | var x = Math.floor (points[offset]); 37 | var y = Math.floor( points[offset + 1]); 38 | if (x < - 1 || x > width || y < - 1 || y > height) 39 | { 40 | throw "Error.checkAndNudgePoints "; 41 | } 42 | nudged = false; 43 | if (x == - 1) 44 | { 45 | points[offset] = 0.0; 46 | nudged = true; 47 | } 48 | else if (x == width) 49 | { 50 | points[offset] = width - 1; 51 | nudged = true; 52 | } 53 | if (y == - 1) 54 | { 55 | points[offset + 1] = 0.0; 56 | nudged = true; 57 | } 58 | else if (y == height) 59 | { 60 | points[offset + 1] = height - 1; 61 | nudged = true; 62 | } 63 | } 64 | // Check and nudge points from end: 65 | nudged = true; 66 | for (var offset = points.length - 2; offset >= 0 && nudged; offset -= 2) 67 | { 68 | var x = Math.floor( points[offset]); 69 | var y = Math.floor( points[offset + 1]); 70 | if (x < - 1 || x > width || y < - 1 || y > height) 71 | { 72 | throw "Error.checkAndNudgePoints "; 73 | } 74 | nudged = false; 75 | if (x == - 1) 76 | { 77 | points[offset] = 0.0; 78 | nudged = true; 79 | } 80 | else if (x == width) 81 | { 82 | points[offset] = width - 1; 83 | nudged = true; 84 | } 85 | if (y == - 1) 86 | { 87 | points[offset + 1] = 0.0; 88 | nudged = true; 89 | } 90 | else if (y == height) 91 | { 92 | points[offset + 1] = height - 1; 93 | nudged = true; 94 | } 95 | } 96 | } 97 | 98 | 99 | 100 | GridSampler.sampleGrid3=function( image, dimension, transform) 101 | { 102 | var bits = new BitMatrix(dimension); 103 | var points = new Array(dimension << 1); 104 | for (var y = 0; y < dimension; y++) 105 | { 106 | var max = points.length; 107 | var iValue = y + 0.5; 108 | for (var x = 0; x < max; x += 2) 109 | { 110 | points[x] = (x >> 1) + 0.5; 111 | points[x + 1] = iValue; 112 | } 113 | transform.transformPoints1(points); 114 | // Quick check to see if points transformed to something inside the image; 115 | // sufficient to check the endpoints 116 | GridSampler.checkAndNudgePoints(image, points); 117 | try 118 | { 119 | for (var x = 0; x < max; x += 2) 120 | { 121 | var xpoint = (Math.floor( points[x]) * 4) + (Math.floor( points[x + 1]) * qrcode.width * 4); 122 | var bit = image[Math.floor( points[x])+ qrcode.width* Math.floor( points[x + 1])]; 123 | qrcode.imagedata.data[xpoint] = bit?255:0; 124 | qrcode.imagedata.data[xpoint+1] = bit?255:0; 125 | qrcode.imagedata.data[xpoint+2] = 0; 126 | qrcode.imagedata.data[xpoint+3] = 255; 127 | //bits[x >> 1][ y]=bit; 128 | if(bit) 129 | bits.set_Renamed(x >> 1, y); 130 | } 131 | } 132 | catch ( aioobe) 133 | { 134 | // This feels wrong, but, sometimes if the finder patterns are misidentified, the resulting 135 | // transform gets "twisted" such that it maps a straight line of points to a set of points 136 | // whose endpoints are in bounds, but others are not. There is probably some mathematical 137 | // way to detect this about the transformation that I don't know yet. 138 | // This results in an ugly runtime exception despite our clever checks above -- can't have 139 | // that. We could check each point's coordinates but that feels duplicative. We settle for 140 | // catching and wrapping ArrayIndexOutOfBoundsException. 141 | throw "Error.checkAndNudgePoints"; 142 | } 143 | } 144 | return bits; 145 | } 146 | 147 | GridSampler.sampleGridx=function( image, dimension, p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY) 148 | { 149 | var transform = PerspectiveTransform.quadrilateralToQuadrilateral(p1ToX, p1ToY, p2ToX, p2ToY, p3ToX, p3ToY, p4ToX, p4ToY, p1FromX, p1FromY, p2FromX, p2FromY, p3FromX, p3FromY, p4FromX, p4FromY); 150 | 151 | return GridSampler.sampleGrid3(image, dimension, transform); 152 | } 153 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/qrcode.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2011 Lazar Laszlo (lazarsoft@gmail.com, www.lazarsoft.info) 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | 18 | qrcode = {}; 19 | qrcode.imagedata = null; 20 | qrcode.width = 0; 21 | qrcode.height = 0; 22 | qrcode.qrCodeSymbol = null; 23 | qrcode.debug = false; 24 | qrcode.maxImgSize = 1024*1024; 25 | 26 | qrcode.sizeOfDataLengthInfo = [ [ 10, 9, 8, 8 ], [ 12, 11, 16, 10 ], [ 14, 13, 16, 12 ] ]; 27 | 28 | qrcode.callback = null; 29 | 30 | qrcode.decode = function(width, height, imageData){ 31 | 32 | qrcode.width = width; 33 | qrcode.height = height; 34 | qrcode.imagedata = imageData; 35 | 36 | return qrcode.process(); 37 | } 38 | 39 | qrcode.isUrl = function(s) 40 | { 41 | var regexp = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; 42 | return regexp.test(s); 43 | } 44 | 45 | qrcode.decode_url = function (s) 46 | { 47 | var escaped = ""; 48 | try{ 49 | escaped = escape( s ); 50 | } 51 | catch(e) 52 | { 53 | console.log(e); 54 | escaped = s; 55 | } 56 | var ret = ""; 57 | try{ 58 | ret = decodeURIComponent( escaped ); 59 | } 60 | catch(e) 61 | { 62 | console.log(e); 63 | ret = escaped; 64 | } 65 | return ret; 66 | } 67 | 68 | qrcode.decode_utf8 = function ( s ) 69 | { 70 | if(qrcode.isUrl(s)) 71 | return qrcode.decode_url(s); 72 | else 73 | return s; 74 | } 75 | 76 | qrcode.process = function(){ 77 | 78 | var image = qrcode.grayScaleToBitmap(qrcode.grayscale()); 79 | var detector = new Detector(image); 80 | 81 | var qRCodeMatrix = detector.detect(); 82 | 83 | 84 | var reader = Decoder.decode(qRCodeMatrix.bits); 85 | var data = reader.DataByte; 86 | var str=""; 87 | for(var i=0;i minmax[ax][ay][1]) 150 | minmax[ax][ay][1] = target; 151 | } 152 | } 153 | //minmax[ax][ay][0] = (minmax[ax][ay][0] + minmax[ax][ay][1]) / 2; 154 | } 155 | } 156 | var middle = new Array(numSqrtArea); 157 | for (var i3 = 0; i3 < numSqrtArea; i3++) 158 | { 159 | middle[i3] = new Array(numSqrtArea); 160 | } 161 | for (var ay = 0; ay < numSqrtArea; ay++) 162 | { 163 | for (var ax = 0; ax < numSqrtArea; ax++) 164 | { 165 | middle[ax][ay] = Math.floor((minmax[ax][ay][0] + minmax[ax][ay][1]) / 2); 166 | //Console.out.print(middle[ax][ay] + ","); 167 | } 168 | //Console.out.println(""); 169 | } 170 | //Console.out.println(""); 171 | 172 | return middle; 173 | } 174 | 175 | qrcode.grayScaleToBitmap=function(grayScale) 176 | { 177 | var middle = qrcode.getMiddleBrightnessPerArea(grayScale); 178 | var sqrtNumArea = middle.length; 179 | var areaWidth = Math.floor(qrcode.width / sqrtNumArea); 180 | var areaHeight = Math.floor(qrcode.height / sqrtNumArea); 181 | var bitmap = new Array(qrcode.height*qrcode.width); 182 | 183 | for (var ay = 0; ay < sqrtNumArea; ay++) 184 | { 185 | for (var ax = 0; ax < sqrtNumArea; ax++) 186 | { 187 | for (var dy = 0; dy < areaHeight; dy++) 188 | { 189 | for (var dx = 0; dx < areaWidth; dx++) 190 | { 191 | bitmap[areaWidth * ax + dx+ (areaHeight * ay + dy)*qrcode.width] = (grayScale[areaWidth * ax + dx+ (areaHeight * ay + dy)*qrcode.width] < middle[ax][ay])?true:false; 192 | } 193 | } 194 | } 195 | } 196 | return bitmap; 197 | } 198 | 199 | qrcode.grayscale = function(){ 200 | var ret = new Array(qrcode.width*qrcode.height); 201 | for (var y = 0; y < qrcode.height; y++) 202 | { 203 | for (var x = 0; x < qrcode.width; x++) 204 | { 205 | var gray = qrcode.getPixel(x, y); 206 | 207 | ret[x+y*qrcode.width] = gray; 208 | } 209 | } 210 | return ret; 211 | } 212 | 213 | 214 | 215 | 216 | function URShift( number, bits) 217 | { 218 | if (number >= 0) 219 | return number >> bits; 220 | else 221 | return (number >> bits) + (2 << ~bits); 222 | } 223 | 224 | 225 | Array.prototype.remove = function(from, to) { 226 | var rest = this.slice((to || from) + 1 || this.length); 227 | this.length = from < 0 ? this.length + from : from; 228 | return this.push.apply(this, rest); 229 | }; 230 | -------------------------------------------------------------------------------- /app/scripts/jsqrcode/qrworker.js: -------------------------------------------------------------------------------- 1 | importScripts('grid.js', 2 | 'version.js', 3 | 'detector.js', 4 | 'formatinf.js', 5 | 'errorlevel.js', 6 | 'bitmat.js', 7 | 'datablock.js', 8 | 'bmparser.js', 9 | 'datamask.js', 10 | 'rsdecoder.js', 11 | 'gf256poly.js', 12 | 'gf256.js', 13 | 'decoder.js', 14 | 'qrcode.js', 15 | 'findpat.js', 16 | 'alignpat.js', 17 | 'databr.js'); 18 | 19 | 20 | self.onmessage = function(e) { 21 | var data = e.data; 22 | 23 | try { 24 | var width = data.width; 25 | var height = data.height; 26 | var result = qrcode.decode(width, height, data); 27 | postMessage(result); 28 | } 29 | catch(e) { 30 | postMessage(undefined); 31 | } 32 | 33 | }; -------------------------------------------------------------------------------- /app/scripts/jsqrcode/rsdecoder.js: -------------------------------------------------------------------------------- 1 | /* 2 | Ported to JavaScript by Lazar Laszlo 2011 3 | 4 | lazarsoft@gmail.com, www.lazarsoft.info 5 | 6 | */ 7 | 8 | /* 9 | * 10 | * Copyright 2007 ZXing authors 11 | * 12 | * Licensed under the Apache License, Version 2.0 (the "License"); 13 | * you may not use this file except in compliance with the License. 14 | * You may obtain a copy of the License at 15 | * 16 | * http://www.apache.org/licenses/LICENSE-2.0 17 | * 18 | * Unless required by applicable law or agreed to in writing, software 19 | * distributed under the License is distributed on an "AS IS" BASIS, 20 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | * See the License for the specific language governing permissions and 22 | * limitations under the License. 23 | */ 24 | 25 | 26 | function ReedSolomonDecoder(field) 27 | { 28 | this.field = field; 29 | this.decode=function(received, twoS) 30 | { 31 | var poly = new GF256Poly(this.field, received); 32 | var syndromeCoefficients = new Array(twoS); 33 | for(var i=0;i= b's 70 | if (a.Degree < b.Degree) 71 | { 72 | var temp = a; 73 | a = b; 74 | b = temp; 75 | } 76 | 77 | var rLast = a; 78 | var r = b; 79 | var sLast = this.field.One; 80 | var s = this.field.Zero; 81 | var tLast = this.field.Zero; 82 | var t = this.field.One; 83 | 84 | // Run Euclidean algorithm until r's degree is less than R/2 85 | while (r.Degree >= Math.floor(R / 2)) 86 | { 87 | var rLastLast = rLast; 88 | var sLastLast = sLast; 89 | var tLastLast = tLast; 90 | rLast = r; 91 | sLast = s; 92 | tLast = t; 93 | 94 | // Divide rLastLast by rLast, with quotient in q and remainder in r 95 | if (rLast.Zero) 96 | { 97 | // Oops, Euclidean algorithm already terminated? 98 | throw "r_{i-1} was zero"; 99 | } 100 | r = rLastLast; 101 | var q = this.field.Zero; 102 | var denominatorLeadingTerm = rLast.getCoefficient(rLast.Degree); 103 | var dltInverse = this.field.inverse(denominatorLeadingTerm); 104 | while (r.Degree >= rLast.Degree && !r.Zero) 105 | { 106 | var degreeDiff = r.Degree - rLast.Degree; 107 | var scale = this.field.multiply(r.getCoefficient(r.Degree), dltInverse); 108 | q = q.addOrSubtract(this.field.buildMonomial(degreeDiff, scale)); 109 | r = r.addOrSubtract(rLast.multiplyByMonomial(degreeDiff, scale)); 110 | //r.EXE(); 111 | } 112 | 113 | s = q.multiply1(sLast).addOrSubtract(sLastLast); 114 | t = q.multiply1(tLast).addOrSubtract(tLastLast); 115 | } 116 | 117 | var sigmaTildeAtZero = t.getCoefficient(0); 118 | if (sigmaTildeAtZero == 0) 119 | { 120 | throw "ReedSolomonException sigmaTilde(0) was zero"; 121 | } 122 | 123 | var inverse = this.field.inverse(sigmaTildeAtZero); 124 | var sigma = t.multiply2(inverse); 125 | var omega = r.multiply2(inverse); 126 | return new Array(sigma, omega); 127 | } 128 | this.findErrorLocations=function( errorLocator) 129 | { 130 | // This is a direct application of Chien's search 131 | var numErrors = errorLocator.Degree; 132 | if (numErrors == 1) 133 | { 134 | // shortcut 135 | return new Array(errorLocator.getCoefficient(1)); 136 | } 137 | var result = new Array(numErrors); 138 | var e = 0; 139 | for (var i = 1; i < 256 && e < numErrors; i++) 140 | { 141 | if (errorLocator.evaluateAt(i) == 0) 142 | { 143 | result[e] = this.field.inverse(i); 144 | e++; 145 | } 146 | } 147 | if (e != numErrors) 148 | { 149 | throw "Error locator degree does not match number of roots"; 150 | } 151 | return result; 152 | } 153 | this.findErrorMagnitudes=function( errorEvaluator, errorLocations, dataMatrix) 154 | { 155 | // This is directly applying Forney's Formula 156 | var s = errorLocations.length; 157 | var result = new Array(s); 158 | for (var i = 0; i < s; i++) 159 | { 160 | var xiInverse = this.field.inverse(errorLocations[i]); 161 | var denominator = 1; 162 | for (var j = 0; j < s; j++) 163 | { 164 | if (i != j) 165 | { 166 | denominator = this.field.multiply(denominator, GF256.addOrSubtract(1, this.field.multiply(errorLocations[j], xiInverse))); 167 | } 168 | } 169 | result[i] = this.field.multiply(errorEvaluator.evaluateAt(xiInverse), this.field.inverse(denominator)); 170 | // Thanks to sanfordsquires for this fix: 171 | if (dataMatrix) 172 | { 173 | result[i] = this.field.multiply(result[i], xiInverse); 174 | } 175 | } 176 | return result; 177 | } 178 | } -------------------------------------------------------------------------------- /app/scripts/layout/layout.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Class constructor for Layout WSK component. 3 | * Implements WSK component design pattern defined at: 4 | * https://github.com/jasonmayes/wsk-component-design-pattern 5 | * @param {HTMLElement} element The element that will be upgraded. 6 | */ 7 | function MaterialLayout(element) { 8 | 'use strict'; 9 | 10 | this.element_ = element; 11 | 12 | // Initialize instance. 13 | this.init(); 14 | } 15 | 16 | /** 17 | * Store constants in one place so they can be updated easily. 18 | * @enum {string | number} 19 | * @private 20 | */ 21 | MaterialLayout.prototype.Constant_ = { 22 | MAX_WIDTH: '(max-width: 850px)' 23 | }; 24 | 25 | /** 26 | * Modes. 27 | * @enum {number} 28 | * @private 29 | */ 30 | MaterialLayout.prototype.Mode_ = { 31 | STANDARD: 0, 32 | SEAMED: 1, 33 | WATERFALL: 2, 34 | SCROLL: 3 35 | }; 36 | 37 | /** 38 | * Store strings for class names defined by this component that are used in 39 | * JavaScript. This allows us to simply change it in one place should we 40 | * decide to modify at a later date. 41 | * @enum {string} 42 | * @private 43 | */ 44 | MaterialLayout.prototype.CssClasses_ = { 45 | HEADER: 'wsk-layout__header', 46 | DRAWER: 'wsk-layout__drawer', 47 | CONTENT: 'wsk-layout__content', 48 | DRAWER_BTN: 'wsk-layout__drawer-button', 49 | 50 | JS_RIPPLE_EFFECT: 'wsk-js-ripple-effect', 51 | RIPPLE_CONTAINER: 'wsk-layout__tab-ripple-container', 52 | RIPPLE: 'wsk-ripple', 53 | RIPPLE_IGNORE_EVENTS: 'wsk-js-ripple-effect--ignore-events', 54 | 55 | HEADER_SEAMED: 'wsk-layout__header--seamed', 56 | HEADER_WATERFALL: 'wsk-layout__header--waterfall', 57 | HEADER_SCROLL: 'wsk-layout__header--scroll', 58 | 59 | FIXED_HEADER: 'wsk-layout--fixed-header', 60 | OBFUSCATOR: 'wsk-layout__obfuscator', 61 | 62 | TAB_BAR: 'wsk-layout__tab-bar', 63 | TAB_CONTAINER: 'wsk-layout__tab-bar-container', 64 | TAB: 'wsk-layout__tab', 65 | TAB_BAR_BUTTON: 'wsk-layout__tab-bar-button', 66 | TAB_BAR_LEFT_BUTTON: 'wsk-layout__tab-bar-left-button', 67 | TAB_BAR_RIGHT_BUTTON: 'wsk-layout__tab-bar-right-button', 68 | PANEL: 'wsk-layout__tab-panel', 69 | 70 | SHADOW_CLASS: 'is-casting-shadow', 71 | COMPACT_CLASS: 'is-compact', 72 | SMALL_SCREEN_CLASS: 'is-small-screen', 73 | DRAWER_OPEN_CLASS: 'is-visible', 74 | ACTIVE_CLASS: 'is-active', 75 | UPGRADED_CLASS: 'is-upgraded' 76 | }; 77 | 78 | /** 79 | * Handles scrolling on the content. 80 | * @private 81 | */ 82 | MaterialLayout.prototype.contentScrollHandler_ = function() { 83 | 'use strict'; 84 | 85 | if (this.content_.scrollTop > 0) { 86 | this.header_.classList.add(this.CssClasses_.SHADOW_CLASS); 87 | this.header_.classList.add(this.CssClasses_.COMPACT_CLASS); 88 | } else { 89 | this.header_.classList.remove(this.CssClasses_.SHADOW_CLASS); 90 | this.header_.classList.remove(this.CssClasses_.COMPACT_CLASS); 91 | } 92 | }; 93 | 94 | /** 95 | * Handles changes in screen size. 96 | * @private 97 | */ 98 | MaterialLayout.prototype.screenSizeHandler_ = function() { 99 | 'use strict'; 100 | 101 | if (this.screenSizeMediaQuery_.matches) { 102 | this.element_.classList.add(this.CssClasses_.SMALL_SCREEN_CLASS); 103 | } 104 | else { 105 | this.element_.classList.remove(this.CssClasses_.SMALL_SCREEN_CLASS); 106 | // Collapse drawer (if any) when moving to a large screen size. 107 | if (this.drawer_) { 108 | this.drawer_.classList.remove(this.CssClasses_.DRAWER_OPEN_CLASS); 109 | } 110 | } 111 | }; 112 | 113 | /** 114 | * Handles toggling of the drawer. 115 | * @param {Element} drawer The drawer container element. 116 | * @private 117 | */ 118 | MaterialLayout.prototype.drawerToggleHandler_ = function() { 119 | 'use strict'; 120 | 121 | this.drawer_.classList.toggle(this.CssClasses_.DRAWER_OPEN_CLASS); 122 | }; 123 | 124 | /** 125 | * Reset tab state, dropping active classes 126 | * @private 127 | */ 128 | MaterialLayout.prototype.resetTabState_ = function(tabBar) { 129 | 'use strict'; 130 | 131 | for (var k = 0; k < tabBar.length; k++) { 132 | tabBar[k].classList.remove(this.CssClasses_.ACTIVE_CLASS); 133 | } 134 | }; 135 | 136 | /** 137 | * Reset panel state, droping active classes 138 | * @private 139 | */ 140 | MaterialLayout.prototype.resetPanelState_ = function(panels) { 141 | 'use strict'; 142 | 143 | for (var j = 0; j < panels.length; j++) { 144 | panels[j].classList.remove(this.CssClasses_.ACTIVE_CLASS); 145 | } 146 | }; 147 | 148 | /** 149 | * Initialize element. 150 | */ 151 | MaterialLayout.prototype.init = function() { 152 | 'use strict'; 153 | 154 | if (this.element_) { 155 | var container = document.createElement('div'); 156 | container.classList.add('wsk-layout__container'); 157 | this.element_.parentElement.insertBefore(container, this.element_); 158 | this.element_.parentElement.removeChild(this.element_); 159 | container.appendChild(this.element_); 160 | 161 | this.header_ = this.element_.querySelector('.' + this.CssClasses_.HEADER); 162 | this.drawer_ = this.element_.querySelector('.' + this.CssClasses_.DRAWER); 163 | this.tabBar_ = this.element_.querySelector('.' + this.CssClasses_.TAB_BAR); 164 | this.content_ = this.element_.querySelector('.' + this.CssClasses_.CONTENT); 165 | 166 | var mode = this.Mode_.STANDARD; 167 | 168 | // Keep an eye on screen size, and add/remove auxiliary class for styling 169 | // of small screens. 170 | this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH); 171 | this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)); 172 | this.screenSizeHandler_(); 173 | 174 | if (this.header_) { 175 | if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) { 176 | mode = this.Mode_.SEAMED; 177 | } else if (this.header_.classList.contains( 178 | this.CssClasses_.HEADER_WATERFALL)) { 179 | mode = this.Mode_.WATERFALL; 180 | } else if (this.element_.classList.contains( 181 | this.CssClasses_.HEADER_SCROLL)) { 182 | mode = this.Mode_.SCROLL; 183 | } 184 | 185 | if (mode === this.Mode_.STANDARD) { 186 | this.header_.classList.add(this.CssClasses_.SHADOW_CLASS); 187 | if (this.tabBar_) { 188 | this.tabBar_.classList.add(this.CssClasses_.SHADOW_CLASS); 189 | } 190 | } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) { 191 | this.header_.classList.remove(this.CssClasses_.SHADOW_CLASS); 192 | if (this.tabBar_) { 193 | this.tabBar_.classList.remove(this.CssClasses_.SHADOW_CLASS); 194 | } 195 | } else if (mode === this.Mode_.WATERFALL) { 196 | // Add and remove shadows depending on scroll position. 197 | // Also add/remove auxiliary class for styling of the compact version of 198 | // the header. 199 | this.content_.addEventListener('scroll', 200 | this.contentScrollHandler_.bind(this)); 201 | this.contentScrollHandler_(); 202 | } 203 | } 204 | 205 | // Add drawer toggling button to our layout, if we have an openable drawer. 206 | if (this.drawer_) { 207 | var drawerButton = document.createElement('div'); 208 | drawerButton.classList.add(this.CssClasses_.DRAWER_BTN); 209 | drawerButton.addEventListener('click', 210 | this.drawerToggleHandler_.bind(this)); 211 | 212 | // If we have a fixed header, add the button to the header rather than 213 | // the layout. 214 | if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) { 215 | this.header_.insertBefore(drawerButton, this.header_.firstChild); 216 | } else { 217 | this.element_.insertBefore(drawerButton, this.content_); 218 | } 219 | 220 | var obfuscator = document.createElement('div'); 221 | obfuscator.classList.add(this.CssClasses_.OBFUSCATOR); 222 | this.element_.appendChild(obfuscator); 223 | obfuscator.addEventListener('click', 224 | this.drawerToggleHandler_.bind(this)); 225 | } 226 | 227 | // Initialize tabs, if any. 228 | if (this.tabBar_) { 229 | var tabContainer = document.createElement('div'); 230 | tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER); 231 | this.element_.insertBefore(tabContainer, this.tabBar_); 232 | this.element_.removeChild(this.tabBar_); 233 | 234 | var leftButton = document.createElement('div'); 235 | leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); 236 | leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON); 237 | leftButton.addEventListener('click', function() { 238 | this.tabBar_.scrollLeft -= 100; 239 | }.bind(this)); 240 | 241 | var rightButton = document.createElement('div'); 242 | rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); 243 | rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON); 244 | rightButton.addEventListener('click', function() { 245 | this.tabBar_.scrollLeft += 100; 246 | }.bind(this)); 247 | 248 | tabContainer.appendChild(leftButton); 249 | tabContainer.appendChild(this.tabBar_); 250 | tabContainer.appendChild(rightButton); 251 | 252 | // Add and remove buttons depending on scroll position. 253 | var tabScrollHandler = function() { 254 | if (this.tabBar_.scrollLeft > 0) { 255 | leftButton.classList.add(this.CssClasses_.ACTIVE_CLASS); 256 | } else { 257 | leftButton.classList.remove(this.CssClasses_.ACTIVE_CLASS); 258 | } 259 | 260 | if (this.tabBar_.scrollLeft < 261 | this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) { 262 | rightButton.classList.add(this.CssClasses_.ACTIVE_CLASS); 263 | } else { 264 | rightButton.classList.remove(this.CssClasses_.ACTIVE_CLASS); 265 | } 266 | }.bind(this); 267 | 268 | this.tabBar_.addEventListener('scroll', tabScrollHandler); 269 | tabScrollHandler(); 270 | 271 | if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { 272 | this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 273 | } 274 | 275 | // Select element tabs, document panels 276 | var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB); 277 | var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL); 278 | 279 | // Create new tabs for each tab element 280 | for (var i = 0; i < tabs.length; i++) { 281 | new MaterialLayoutTab(tabs[i], tabs, panels, this); 282 | } 283 | } 284 | 285 | this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS); 286 | } 287 | }; 288 | 289 | function MaterialLayoutTab(tab, tabs, panels, layout) { 290 | 'use strict'; 291 | 292 | if (tab) { 293 | if (layout.tabBar_.classList.contains( 294 | layout.CssClasses_.JS_RIPPLE_EFFECT)) { 295 | var rippleContainer = document.createElement('span'); 296 | rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER); 297 | rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT); 298 | var ripple = document.createElement('span'); 299 | ripple.classList.add(layout.CssClasses_.RIPPLE); 300 | rippleContainer.appendChild(ripple); 301 | tab.appendChild(rippleContainer); 302 | } 303 | 304 | tab.addEventListener('click', function(e) { 305 | e.preventDefault(); 306 | var href = tab.href.split('#')[1]; 307 | var panel = layout.content_.querySelector('#' + href); 308 | layout.resetTabState_(tabs); 309 | layout.resetPanelState_(panels); 310 | tab.classList.add(layout.CssClasses_.ACTIVE_CLASS); 311 | panel.classList.add(layout.CssClasses_.ACTIVE_CLASS); 312 | }); 313 | 314 | } 315 | } 316 | 317 | // The component registers itself. It can assume componentHandler is available 318 | // in the global scope. 319 | componentHandler.register({ 320 | constructor: MaterialLayout, 321 | classAsString: 'MaterialLayout', 322 | cssClass: 'wsk-js-layout' 323 | }); 324 | -------------------------------------------------------------------------------- /app/scripts/main.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * QR Code scanner. 4 | * Copyright 2015 Google Inc. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License 17 | * 18 | */ 19 | (function() { 20 | 'use strict'; 21 | 22 | var QRCodeCamera = function(element) { 23 | // Controls the Camera and the QRCode Module 24 | 25 | var cameraManager = new CameraManager('camera'); 26 | var qrCodeManager = new QRCodeManager('qrcode'); 27 | 28 | 29 | cameraManager.onframe = function() { 30 | // There is a frame in the camera, what should we do with it? 31 | 32 | var imageData = cameraManager.getImageData(); 33 | var detectedQRCode = qrCodeManager.detectQRCode(imageData, function(url) { 34 | if(url !== undefined) { 35 | qrCodeManager.showDialog(url); 36 | } 37 | }); 38 | 39 | }; 40 | }; 41 | 42 | var QRCodeManager = function(element) { 43 | var root = document.getElementById(element); 44 | var canvas = document.getElementById("qr-canvas"); 45 | var qrcodeData = root.querySelector(".QRCodeSuccessDialog-data"); 46 | var qrcodeNavigate = root.querySelector(".QRCodeSuccessDialog-navigate"); 47 | var qrcodeIgnore = root.querySelector(".QRCodeSuccessDialog-ignore"); 48 | 49 | var client = new QRClient(); 50 | 51 | var self = this; 52 | 53 | this.currentUrl = undefined; 54 | 55 | 56 | this.detectQRCode = function(imageData, callback) { 57 | callback = callback || function() {}; 58 | 59 | client.decode(imageData, function(result) { 60 | if(result !== undefined) { 61 | self.currentUrl = result; 62 | } 63 | callback(result); 64 | }); 65 | }; 66 | 67 | this.showDialog = function(url) { 68 | root.style.display = 'block'; 69 | qrcodeData.innerText = url; 70 | }; 71 | 72 | this.closeDialog = function() { 73 | root.style.display = 'none'; 74 | self.qrcodeNavigate = ""; 75 | qrcodeData.innerText = ""; 76 | }; 77 | 78 | qrcodeIgnore.addEventListener("click", function() { 79 | self.closeDialog(); 80 | }.bind(this)); 81 | 82 | qrcodeNavigate.addEventListener("click", function() { 83 | // I really want this to be a link. 84 | window.location = this.currentUrl; 85 | this.closeDialog(); 86 | }.bind(this)); 87 | 88 | }; 89 | 90 | var CameraManager = function(element) { 91 | // The camera gets a video stream, and adds it to a canvas. 92 | // The canvas is analysed but also displayed to the user. 93 | // The video is never show 94 | 95 | var self = this; 96 | 97 | var cameraRoot = document.getElementById(element); 98 | var cameraVideo = cameraRoot.querySelector('.Camera-video'); 99 | var cameraCanvas = cameraRoot.querySelector('.Camera-display'); 100 | var cameraToggle = cameraRoot.querySelector('.Camera-toggle'); 101 | var cameraOverlay = cameraRoot.querySelector('.Camera-overlay'); 102 | var cameraToggleInput = cameraRoot.querySelector('.Camera-toggle-input'); 103 | 104 | var canvas = cameraCanvas.getContext('2d'); 105 | 106 | // Variables 107 | var dWidth; 108 | var dHeight; 109 | var dx = 0; 110 | var dy = 0; 111 | 112 | var sx = 0; 113 | var sy = 0; 114 | var sHeight; 115 | var sWidth; 116 | var scaleX; 117 | var scaleY; 118 | var scaleFactor = 1; 119 | 120 | var cameras = []; 121 | var coordinatesHaveChanged = false; 122 | var prevCoordinates = 0; 123 | // The camera stream. 124 | var localStream; 125 | 126 | var overlayCoords = { x:0, y: 0, width: cameraCanvas.width, height: cameraCanvas.height }; 127 | 128 | this.getImageData = function() { 129 | // Only get the image data for what we will send to the detector. 130 | return canvas.getImageData(overlayCoords.x, overlayCoords.y, overlayCoords.width, overlayCoords.height); 131 | }; 132 | 133 | var drawOverlay = function(width, height) { 134 | 135 | var minLength = Math.min(width, height); 136 | 137 | var boxHeightSize = (height + 64 - minLength) / 2; 138 | var boxWidthSize = (width + 64 - minLength) / 2; 139 | 140 | if(coordinatesHaveChanged) { 141 | 142 | cameraOverlay.style.borderTopWidth = boxHeightSize + "px"; 143 | cameraOverlay.style.borderLeftWidth = boxWidthSize + "px"; 144 | cameraOverlay.style.borderRightWidth = boxWidthSize + "px"; 145 | cameraOverlay.style.borderBottomWidth = boxHeightSize + "px"; 146 | 147 | overlayCoords.x = boxWidthSize; 148 | overlayCoords.y = boxHeightSize 149 | overlayCoords.width = cameraCanvas.width - (boxWidthSize * 2); 150 | overlayCoords.height = cameraCanvas.height - (boxHeightSize * 2); 151 | 152 | } 153 | 154 | }; 155 | 156 | var setupVariables = function(e) { 157 | dWidth = cameraCanvas.width = window.innerWidth; 158 | dHeight = cameraCanvas.height = window.innerHeight; 159 | dx = 0; 160 | dy = 0; 161 | 162 | sx = 0; 163 | sy = 0; 164 | 165 | // Make the video coordinate space the same as the window. 166 | // size in the longest dimension. 167 | // Then center and clip. and map back to correct space. 168 | scaleX = (dWidth / cameraVideo.videoWidth); 169 | scaleY = (dHeight / cameraVideo.videoHeight); 170 | scaleFactor = Math.max(scaleX, scaleY); 171 | 172 | // Trim the left 173 | sx = ((cameraVideo.videoWidth * scaleFactor) / 2) - (dWidth/ 2); 174 | sy = ((cameraVideo.videoHeight * scaleFactor) / 2) - (dHeight / 2); 175 | 176 | // Trim the right. 177 | sWidth = (cameraVideo.videoWidth * scaleFactor) - sx * 2; 178 | sHeight = (cameraVideo.videoHeight * scaleFactor) - sy * 2; 179 | 180 | return (cameraVideo.videoWidth > 0); 181 | }; 182 | 183 | var captureFrame = function() { 184 | 185 | // Work out which part of the video to capture and apply to canvas. 186 | canvas.drawImage(cameraVideo, sx /scaleFactor, sy/scaleFactor, sWidth/scaleFactor, sHeight/scaleFactor, dx, dy, dWidth, dHeight); 187 | 188 | drawOverlay(dWidth, dHeight, scaleFactor); 189 | 190 | // A frame has been captured. 191 | if(self.onframe) self.onframe(); 192 | 193 | coordinatesHaveChanged = false; 194 | }; 195 | 196 | var getCamera = function(videoSource, cb) { 197 | 198 | cb = cb || function() {}; 199 | 200 | var gUM = (navigator.getUserMedia || 201 | navigator.webkitGetUserMedia || 202 | navigator.mozGetUserMedia || 203 | navigator.msGetUserMedia || null); 204 | 205 | var params; 206 | 207 | if(videoSource === undefined && cameras.length == 0) { 208 | // Because we have no source information, have to assume it user facing. 209 | params = { video: true }; 210 | } 211 | else { 212 | params = { video: { optional: [{sourceId: videoSource.id}] } }; 213 | } 214 | 215 | gUM.call(navigator, params, function(theStream) { 216 | localStream = theStream; 217 | 218 | cameraVideo.onloadeddata = function(e) { 219 | 220 | coordinatesHaveChanged = true; 221 | 222 | var isSetup = setupVariables(e); 223 | if(isSetup) { 224 | setInterval(captureFrame.bind(self), 4); 225 | } 226 | else { 227 | // This is just to get around the fact that the videoWidth is not 228 | // available in Firefox until sometime after the data has loaded. 229 | setTimeout(function() { 230 | setupVariables(e); 231 | 232 | setInterval(captureFrame.bind(self), 4); 233 | }, 100); 234 | } 235 | 236 | // The video is ready, and the camerea captured 237 | if(videoSource === undefined) { 238 | // There is no meta data about the camera, assume user facing. 239 | videoSource = { 240 | 'facing': 'user' 241 | }; 242 | } 243 | 244 | cb(videoSource); 245 | }; 246 | 247 | cameraVideo.src = window.URL.createObjectURL(localStream); 248 | cameraVideo.load(); 249 | cameraVideo.play(); 250 | }, function(error) {}); 251 | }; 252 | 253 | var getSources = function(cb) { 254 | cb = cb || function() {}; 255 | 256 | if('getSources' in MediaStreamTrack) { 257 | MediaStreamTrack.getSources(function(sources) { 258 | 259 | for(var i = 0; i < sources.length; i++) { 260 | var source = sources[i]; 261 | if(source.kind === 'video') { 262 | 263 | if(source.facing === 'environment') { 264 | // cameras facing the environment are pushed to the front of the page 265 | cameras.unshift(source); 266 | } 267 | else { 268 | cameras.push(source); 269 | } 270 | } 271 | } 272 | 273 | if(cameras.length == 1) { 274 | cameraToggle.style.display="none"; 275 | } 276 | 277 | cb(); 278 | }); 279 | } 280 | else { 281 | // We can't pick the correct camera because the API doesn't support it. 282 | cb(); 283 | } 284 | }; 285 | 286 | var toggleFacingState = function(camera) { 287 | var facing = camera.facing ? camera.facing : 'user'; 288 | cameraRoot.classList.remove('Camera--facing-environment'); 289 | cameraRoot.classList.remove('Camera--facing-user'); 290 | cameraRoot.classList.add('Camera--facing-' + facing); 291 | }; 292 | 293 | cameraToggleInput.addEventListener('change', function() { 294 | // this is the input element, not the control 295 | var cameraIdx = 0; 296 | 297 | if(this.checked === true) { 298 | cameraIdx = 1; 299 | } 300 | 301 | getCamera(cameras[cameraIdx], toggleFacingState); 302 | 303 | }); 304 | 305 | window.addEventListener('resize', function() { 306 | coordinatesHaveChanged = true; 307 | setupVariables(); 308 | }.bind(this)); 309 | 310 | document.addEventListener('visibilitychange', function(e) { 311 | if(document.visibilityState === 'hidden') { 312 | // Disconnect the camera. 313 | if(localStream !== undefined) { 314 | localStream.stop(); 315 | localStream = undefined; 316 | } 317 | } 318 | else { 319 | var cameraIdx = 0; 320 | 321 | if(this.checked === true) { 322 | cameraIdx = 1; 323 | } 324 | 325 | getCamera(cameras[cameraIdx], toggleFacingState); 326 | } 327 | }); 328 | 329 | // Init 330 | getSources(function() { 331 | // On first run, select the first camera. 332 | getCamera(cameras[0], toggleFacingState); 333 | }); 334 | 335 | }; 336 | 337 | window.addEventListener('load', function() { 338 | var camera = new QRCodeCamera(); 339 | }); 340 | })(); 341 | -------------------------------------------------------------------------------- /app/scripts/qrclient.js: -------------------------------------------------------------------------------- 1 | var QRClient = function() { 2 | 3 | var currentCallback; 4 | 5 | this.decode = function(imageData, callback) { 6 | try { 7 | var width = imageData.width; 8 | var height = imageData.height; 9 | var result = qrcode.decode(width, height, imageData); 10 | callback(result); 11 | } 12 | catch(e) { 13 | // consume the error. 14 | console.log(e) 15 | } 16 | }; 17 | }; -------------------------------------------------------------------------------- /app/scripts/resets/_h5bp.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * What follows is the result of much research on cross-browser styling. 3 | * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, 4 | * Kroc Camen, and the H5BP dev community and team. 5 | */ 6 | 7 | /* ========================================================================== 8 | Base styles: opinionated defaults 9 | ========================================================================== */ 10 | 11 | html { 12 | color: #222; 13 | font-size: 1em; 14 | line-height: 1.4; 15 | } 16 | 17 | /* 18 | * Remove text-shadow in selection highlight: h5bp.com/i 19 | * These selection rule sets have to be separate. 20 | * Customize the background color to match your design. 21 | */ 22 | 23 | ::-moz-selection { 24 | background: #b3d4fc; 25 | text-shadow: none; 26 | } 27 | 28 | ::selection { 29 | background: #b3d4fc; 30 | text-shadow: none; 31 | } 32 | 33 | /* 34 | * A better looking default horizontal rule 35 | */ 36 | 37 | hr { 38 | display: block; 39 | height: 1px; 40 | border: 0; 41 | border-top: 1px solid #ccc; 42 | margin: 1em 0; 43 | padding: 0; 44 | } 45 | 46 | /* 47 | * Remove the gap between images, videos, audio and canvas and the bottom of 48 | * their containers: h5bp.com/i/440 49 | */ 50 | 51 | audio, 52 | canvas, 53 | img, 54 | svg, 55 | video { 56 | vertical-align: middle; 57 | } 58 | 59 | /* 60 | * Remove default fieldset styles. 61 | */ 62 | 63 | fieldset { 64 | border: 0; 65 | margin: 0; 66 | padding: 0; 67 | } 68 | 69 | /* 70 | * Allow only vertical resizing of textareas. 71 | */ 72 | 73 | textarea { 74 | resize: vertical; 75 | } 76 | 77 | /* ========================================================================== 78 | Browse Happy prompt 79 | ========================================================================== */ 80 | 81 | .browsehappy { 82 | margin: 0.2em 0; 83 | background: #ccc; 84 | color: #000; 85 | padding: 0.2em 0; 86 | } 87 | 88 | /* ========================================================================== 89 | Author's custom styles 90 | ========================================================================== */ 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | /* ========================================================================== 109 | Helper classes 110 | ========================================================================== */ 111 | 112 | /* 113 | * Hide visually and from screen readers: h5bp.com/u 114 | */ 115 | 116 | .hidden { 117 | display: none !important; 118 | visibility: hidden; 119 | } 120 | 121 | /* 122 | * Hide only visually, but have it available for screen readers: h5bp.com/v 123 | */ 124 | 125 | .visuallyhidden { 126 | border: 0; 127 | clip: rect(0 0 0 0); 128 | height: 1px; 129 | margin: -1px; 130 | overflow: hidden; 131 | padding: 0; 132 | position: absolute; 133 | width: 1px; 134 | } 135 | 136 | /* 137 | * Extends the .visuallyhidden class to allow the element to be focusable 138 | * when navigated to via the keyboard: h5bp.com/p 139 | */ 140 | 141 | .visuallyhidden.focusable:active, 142 | .visuallyhidden.focusable:focus { 143 | clip: auto; 144 | height: auto; 145 | margin: 0; 146 | overflow: visible; 147 | position: static; 148 | width: auto; 149 | } 150 | 151 | /* 152 | * Hide visually and from screen readers, but maintain layout 153 | */ 154 | 155 | .invisible { 156 | visibility: hidden; 157 | } 158 | 159 | /* 160 | * Clearfix: contain floats 161 | * 162 | * For modern browsers 163 | * 1. The space content is one way to avoid an Opera bug when the 164 | * `contenteditable` attribute is included anywhere else in the document. 165 | * Otherwise it causes space to appear at the top and bottom of elements 166 | * that receive the `clearfix` class. 167 | * 2. The use of `table` rather than `block` is only necessary if using 168 | * `:before` to contain the top-margins of child elements. 169 | */ 170 | 171 | .clearfix:before, 172 | .clearfix:after { 173 | content: " "; /* 1 */ 174 | display: table; /* 2 */ 175 | } 176 | 177 | .clearfix:after { 178 | clear: both; 179 | } 180 | 181 | /* ========================================================================== 182 | EXAMPLE Media Queries for Responsive Design. 183 | These examples override the primary ('mobile first') styles. 184 | Modify as content requires. 185 | ========================================================================== */ 186 | 187 | @media only screen and (min-width: 35em) { 188 | /* Style adjustments for viewports that meet the condition */ 189 | } 190 | 191 | @media print, 192 | (-o-min-device-pixel-ratio: 5/4), 193 | (-webkit-min-device-pixel-ratio: 1.25), 194 | (min-resolution: 120dpi) { 195 | /* Style adjustments for high resolution devices */ 196 | } 197 | 198 | /* ========================================================================== 199 | Print styles. 200 | Inlined to avoid the additional HTTP request: h5bp.com/r 201 | ========================================================================== */ 202 | 203 | @media print { 204 | *, 205 | *:before, 206 | *:after { 207 | background: transparent !important; 208 | color: #000 !important; /* Black prints faster: h5bp.com/s */ 209 | box-shadow: none !important; 210 | text-shadow: none !important; 211 | } 212 | 213 | a, 214 | a:visited { 215 | text-decoration: underline; 216 | } 217 | 218 | a[href]:after { 219 | content: " (" attr(href) ")"; 220 | } 221 | 222 | abbr[title]:after { 223 | content: " (" attr(title) ")"; 224 | } 225 | 226 | /* 227 | * Don't show links that are fragment identifiers, 228 | * or use the `javascript:` pseudo protocol 229 | */ 230 | 231 | a[href^="#"]:after, 232 | a[href^="javascript:"]:after { 233 | content: ""; 234 | } 235 | 236 | pre, 237 | blockquote { 238 | border: 1px solid #999; 239 | page-break-inside: avoid; 240 | } 241 | 242 | thead { 243 | display: table-header-group; /* h5bp.com/t */ 244 | } 245 | 246 | tr, 247 | img { 248 | page-break-inside: avoid; 249 | } 250 | 251 | img { 252 | max-width: 100% !important; 253 | } 254 | 255 | p, 256 | h2, 257 | h3 { 258 | orphans: 3; 259 | widows: 3; 260 | } 261 | 262 | h2, 263 | h3 { 264 | page-break-after: avoid; 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /app/scripts/ripple/ripple.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Class constructor for Ripple WSK component. 3 | * Implements WSK component design pattern defined at: 4 | * https://github.com/jasonmayes/wsk-component-design-pattern 5 | * @param {HTMLElement} element The element that will be upgraded. 6 | */ 7 | function MaterialRipple(element) { 8 | 'use strict'; 9 | 10 | this.element_ = element; 11 | 12 | // Initialize instance. 13 | this.init(); 14 | } 15 | 16 | /** 17 | * Store constants in one place so they can be updated easily. 18 | * @enum {string | number} 19 | * @private 20 | */ 21 | MaterialRipple.prototype.Constant_ = { 22 | INITIAL_SCALE: 'scale(0.0001, 0.0001)', 23 | INITIAL_SIZE: '1px', 24 | INITIAL_OPACITY: '0.4', 25 | FINAL_OPACITY: '0', 26 | FINAL_SCALE: '' 27 | }; 28 | 29 | /** 30 | * Store strings for class names defined by this component that are used in 31 | * JavaScript. This allows us to simply change it in one place should we 32 | * decide to modify at a later date. 33 | * @enum {string} 34 | * @private 35 | */ 36 | MaterialRipple.prototype.CssClasses_ = { 37 | WSK_RIPPLE_CENTER: 'wsk-ripple--center', 38 | 39 | WSK_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'wsk-js-ripple-effect--ignore-events', 40 | 41 | WSK_RIPPLE: 'wsk-ripple', 42 | 43 | IS_ANIMATING: 'is-animating' 44 | }; 45 | 46 | 47 | /** 48 | * Handle click of element. 49 | * @param {Event} event The event that fired. 50 | * @private 51 | */ 52 | MaterialRipple.prototype.downHandler_ = function(event) { 53 | 'use strict'; 54 | 55 | if (event.type === 'mousedown' && this.ignoringMouseDown_) { 56 | this.ignoringMouseDown_ = false; 57 | } else { 58 | if (event.type === 'touchstart') { 59 | this.ignoringMouseDown_ = true; 60 | } 61 | var frameCount = this.getFrameCount(); 62 | if (frameCount > 0) { 63 | return; 64 | } 65 | this.setFrameCount(1); 66 | var bound = event.currentTarget.getBoundingClientRect(); 67 | var x; 68 | var y; 69 | // Check if we are handling a keyboard click. 70 | if (event.clientX === 0 && event.clientY === 0) { 71 | x = Math.round(bound.width / 2); 72 | y = Math.round(bound.height / 2); 73 | } else { 74 | var clientX = event.clientX ? event.clientX : event.touches[0].clientX; 75 | var clientY = event.clientY ? event.clientY : event.touches[0].clientY; 76 | x = Math.round(clientX - bound.left); 77 | y = Math.round(clientY - bound.top); 78 | } 79 | this.setRippleXY(x, y); 80 | this.setRippleStyles(true); 81 | window.requestAnimFrame(this.animFrameHandler.bind(this)); 82 | } 83 | }; 84 | 85 | 86 | /** 87 | * Initialize element. 88 | */ 89 | MaterialRipple.prototype.init = function() { 90 | 'use strict'; 91 | 92 | if (this.element_) { 93 | var recentering = 94 | this.element_.classList.contains(this.CssClasses_.WSK_RIPPLE_CENTER); 95 | if (!this.element_.classList.contains( 96 | this.CssClasses_.WSK_JS_RIPPLE_EFFECT_IGNORE_EVENTS)) { 97 | this.rippleElement_ = this.element_.querySelector('.' + 98 | this.CssClasses_.WSK_RIPPLE); 99 | this.frameCount_ = 0; 100 | this.rippleSize_ = 0; 101 | this.x_ = 0; 102 | this.y_ = 0; 103 | 104 | // Touch start produces a compat mouse down event, which would cause a 105 | // second ripples. To avoid that, we use this property to ignore the first 106 | // mouse down after a touch start. 107 | this.ignoringMouseDown_ = false; 108 | 109 | if (this.rippleElement_) { 110 | var bound = this.element_.getBoundingClientRect(); 111 | this.rippleSize_ = Math.max(bound.width, bound.height) * 2; 112 | this.rippleElement_.style.width = this.rippleSize_ + 'px'; 113 | this.rippleElement_.style.height = this.rippleSize_ + 'px'; 114 | } 115 | 116 | this.element_.addEventListener('mousedown', this.downHandler_.bind(this)); 117 | this.element_.addEventListener('touchstart', 118 | this.downHandler_.bind(this)); 119 | 120 | this.getFrameCount = function() { 121 | return this.frameCount_; 122 | }; 123 | 124 | this.setFrameCount = function(fC) { 125 | this.frameCount_ = fC; 126 | }; 127 | 128 | this.getRippleElement = function() { 129 | return this.rippleElement_; 130 | }; 131 | 132 | this.setRippleXY = function(newX, newY) { 133 | this.x_ = newX; 134 | this.y_ = newY; 135 | }; 136 | 137 | this.setRippleStyles = function(start) { 138 | if (this.rippleElement_ !== null) { 139 | var transformString; 140 | var scale; 141 | var size; 142 | var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)'; 143 | 144 | if (start) { 145 | scale = this.Constant_.INITIAL_SCALE; 146 | size = this.Constant_.INITIAL_SIZE; 147 | } else { 148 | scale = this.Constant_.FINAL_SCALE; 149 | size = this.rippleSize_ + 'px'; 150 | if (recentering) { 151 | offset = 'translate(' + bound.width / 2 + 'px, ' + 152 | bound.height / 2 + 'px)'; 153 | } 154 | } 155 | 156 | transformString = 'translate(-50%, -50%) ' + offset + scale; 157 | 158 | this.rippleElement_.style.webkitTransform = transformString; 159 | this.rippleElement_.style.msTransform = transformString; 160 | this.rippleElement_.style.transform = transformString; 161 | 162 | if (start) { 163 | this.rippleElement_.style.opacity = this.Constant_.INITIAL_OPACITY; 164 | this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING); 165 | } else { 166 | this.rippleElement_.style.opacity = this.Constant_.FINAL_OPACITY; 167 | this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING); 168 | } 169 | } 170 | }; 171 | 172 | this.animFrameHandler = function() { 173 | if (this.frameCount_-- > 0) { 174 | window.requestAnimFrame(this.animFrameHandler.bind(this)); 175 | } else { 176 | this.setRippleStyles(false); 177 | } 178 | }; 179 | } 180 | } 181 | }; 182 | 183 | 184 | // The component registers itself. It can assume componentHandler is available 185 | // in the global scope. 186 | componentHandler.register({ 187 | constructor: MaterialRipple, 188 | classAsString: 'MaterialRipple', 189 | cssClass: 'wsk-js-ripple-effect' 190 | }); 191 | -------------------------------------------------------------------------------- /app/scripts/third_party/rAF.js: -------------------------------------------------------------------------------- 1 | // From: http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/ 2 | // shim layer with setTimeout fallback 3 | window.requestAnimationFrame = (function() { 4 | 'use strict'; 5 | return window.requestAnimationFrame || 6 | window.webkitRequestAnimationFrame || 7 | window.mozRequestAnimationFrame || 8 | function(callback) { 9 | window.setTimeout(callback, 1000 / 60); 10 | }; 11 | })(); 12 | -------------------------------------------------------------------------------- /app/scripts/wskComponentHandler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A component handler interface using the revealing module design pattern. 3 | * More details on this pattern design here: 4 | * https://github.com/jasonmayes/wsk-component-design-pattern 5 | * @author Jason Mayes. 6 | */ 7 | /* exported componentHandler */ 8 | var componentHandler = (function() { 9 | 'use strict'; 10 | 11 | var registeredComponents_ = []; 12 | var createdComponents_ = []; 13 | 14 | 15 | /** 16 | * Searches registered components for a class we are interested in using. 17 | * Optionally replaces a match with passed object if specified. 18 | * @param {string} name The name of a class we want to use. 19 | * @param {object} optReplace Optional object to replace match with. 20 | * @return {object | false} 21 | * @private 22 | */ 23 | function findRegisteredClass_(name, optReplace) { 24 | for (var i = 0; i < registeredComponents_.length; i++) { 25 | if (registeredComponents_[i].className === name) { 26 | if (optReplace !== undefined) { 27 | registeredComponents_[i] = optReplace; 28 | } 29 | return registeredComponents_[i]; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | 36 | /** 37 | * Searches existing DOM for elements of our component type and upgrades them 38 | * if they have not already been upgraded. 39 | * @param {string} jsClass the programatic name of the element class we need 40 | * to create a new instance of. 41 | * @param {string} cssClass the name of the CSS class elements of this type 42 | * will have. 43 | */ 44 | function upgradeDomInternal(jsClass, cssClass) { 45 | if (cssClass === undefined) { 46 | var registeredClass = findRegisteredClass_(jsClass); 47 | if (registeredClass) { 48 | cssClass = registeredClass.cssClass; 49 | } 50 | } 51 | 52 | var elements = document.querySelectorAll('.' + cssClass); 53 | for (var n = 0; n < elements.length; n++) { 54 | upgradeElementInternal(elements[n], jsClass); 55 | } 56 | } 57 | 58 | 59 | /** 60 | * Upgrades a specific element rather than all in the DOM. 61 | * @param {HTMLElement} element The element we wish to upgrade. 62 | * @param {string} jsClass The name of the class we want to upgrade 63 | * the element to. 64 | */ 65 | function upgradeElementInternal(element, jsClass) { 66 | // Only upgrade elements that have not already been upgraded. 67 | var dataUpgraded = element.getAttribute('data-upgraded'); 68 | 69 | if (dataUpgraded === null || dataUpgraded.indexOf(jsClass) === -1) { 70 | // Upgrade element. 71 | if (dataUpgraded === null) { 72 | dataUpgraded = ''; 73 | } 74 | element.setAttribute('data-upgraded', dataUpgraded + ',' + jsClass); 75 | var registeredClass = findRegisteredClass_(jsClass); 76 | if (registeredClass) { 77 | createdComponents_.push(new registeredClass.classConstructor(element)); 78 | // Call any callbacks the user has registered with this component type. 79 | registeredClass.callbacks.forEach(function (callback) { 80 | callback(element); 81 | }); 82 | } else { 83 | // If component creator forgot to register, try and see if 84 | // it is in global scope. 85 | createdComponents_.push(new window[jsClass](element)); 86 | } 87 | } 88 | } 89 | 90 | 91 | /** 92 | * Registers a class for future use and attempts to upgrade existing DOM. 93 | * @param {object} config An object containing: 94 | * {constructor: Constructor, classAsString: string, cssClass: string} 95 | */ 96 | function registerInternal(config) { 97 | var newConfig = { 98 | 'classConstructor': config.constructor, 99 | 'className': config.classAsString, 100 | 'cssClass': config.cssClass, 101 | 'callbacks': [] 102 | }; 103 | 104 | var found = findRegisteredClass_(config.classAsString, newConfig); 105 | 106 | if (!found) { 107 | registeredComponents_.push(newConfig); 108 | } 109 | } 110 | 111 | 112 | /** 113 | * Allows user to be alerted to any upgrades that are performed for a given 114 | * component type 115 | * @param {string} jsClass The class name of the WSK component we wish 116 | * to hook into for any upgrades performed. 117 | * @param {function} callback The function to call upon an upgrade. This 118 | * function should expect 1 parameter - the HTMLElement which got upgraded. 119 | */ 120 | function registerUpgradedCallbackInternal(jsClass, callback) { 121 | var regClass = findRegisteredClass_(jsClass); 122 | if (regClass) { 123 | regClass.callbacks.push(callback); 124 | } 125 | } 126 | 127 | 128 | /** 129 | * Upgrades all registered components found in the current DOM. This is 130 | * automatically called on window load. 131 | */ 132 | function upgradeAllRegisteredInternal() { 133 | for (var n = 0; n < registeredComponents_.length; n++) { 134 | upgradeDomInternal(registeredComponents_[n].className); 135 | } 136 | } 137 | 138 | 139 | // Now return the functions that should be made public with their publicly 140 | // facing names... 141 | return { 142 | upgradeDom: upgradeDomInternal, 143 | upgradeElement: upgradeElementInternal, 144 | upgradeAllRegistered: upgradeAllRegisteredInternal, 145 | registerUpgradedCallback: registerUpgradedCallbackInternal, 146 | register: registerInternal 147 | }; 148 | })(); 149 | 150 | 151 | window.addEventListener('load', function() { 152 | 'use strict'; 153 | 154 | componentHandler.upgradeAllRegistered(); 155 | }); 156 | -------------------------------------------------------------------------------- /app/styles/_colors.scss: -------------------------------------------------------------------------------- 1 | @import "palette/palette"; 2 | 3 | /* ========== Color & Themes ========== */ 4 | 5 | // Use color primarily for emphasis. Choose colors that fit with 6 | // your brand and provide good contrast between visual components. 7 | 8 | $palette-primary: $palette-blue !default; 9 | $palette-accent: $palette-pink !default; 10 | $palette-secondary: $palette-grey !default; 11 | $palette-disabled: $palette-grey !default; 12 | $palette-page: $palette-grey !default; 13 | 14 | /* ========== Typography ========== */ 15 | 16 | // We use the following default color styles: text-color-primary and 17 | // text-color-secondary. For light themes, use text-color-primary-inverse 18 | // and text-color-secondary-inverse. 19 | 20 | $text-color-primary: nth($palette-grey, 9) !default; 21 | $text-color-secondary: nth($palette-grey, 6) !default; 22 | $text-color-primary-inverse: invert($text-color-primary) !default; 23 | $text-color-secondary-inverse: invert($text-color-secondary) !default; 24 | 25 | /* ========== Components ========== */ 26 | 27 | /* ========== Standard Buttons ========== */ 28 | 29 | // Default button colors. 30 | $button-primary-color: rgba(#999999, 0.20) !default; 31 | $button-secondary-color: #000 !default; 32 | $button-hover-color: $button-primary-color !default; 33 | $button-active-color: rgba($button-primary-color, 0.40) !default; 34 | $button-focus-color: rgba(black, 0.12) !default; 35 | 36 | // Colored button colors. 37 | $button-primary-color-alt: nth($palette-primary, 6) !default; 38 | $button-secondary-color-alt: #fff !default; 39 | $button-hover-color-alt: nth($palette-primary, 7) !default; 40 | $button-active-color-alt: nth($palette-primary, 8) !default; 41 | $button-focus-color-alt: $button-focus-color !default; 42 | 43 | // Ripple color for colored raised buttons and FABs. 44 | $button-ripple-color-alt: white !default; 45 | 46 | // Disabled button colors. 47 | $button-primary-color-disabled: rgba(#000, 0.12) !default; 48 | $button-secondary-color-disabled: rgba(#000, 0.26) !default; 49 | 50 | // FAB colors and sizes. 51 | $button-fab-color-alt: nth($palette-accent, 5) !default; 52 | $button-fab-hover-color-alt: nth($palette-accent, 6) !default; 53 | $button-fab-active-color-alt: nth($palette-accent, 7) !default; 54 | 55 | // Icon button colors and sizes. 56 | $button-icon-color: nth($palette-grey, 8) !default; 57 | $button-icon-focus-color: $button-focus-color !default; 58 | 59 | /* ========== Icon Toggles ========== */ 60 | 61 | $icon-toggle-color: nth($palette-grey, 8) !default; 62 | $icon-toggle-focus-color: $button-focus-color !default; 63 | $icon-toggle-checked-color: nth($palette-primary, 6) !default; 64 | $icon-toggle-disabled-color: rgba(#000, 0.26) !default; 65 | 66 | /* ========== Radio Buttons ========== */ 67 | 68 | $radio-color: nth($palette-primary, 6) !default; 69 | $radio-off-color: rgba(0, 0, 0, 0.54) !default; 70 | $radio-disabled-color: rgba(0, 0, 0, 0.26) !default; 71 | 72 | /* ========== Ripple effect ========== */ 73 | 74 | $ripple-bg-color: nth($palette-grey, 10) !default; 75 | 76 | /* ========== Layout ========== */ 77 | 78 | // Drawer 79 | $layout-drawer-bg-color: nth($palette-page, 1) !default; 80 | $layout-drawer-border-color: nth($palette-page, 4) !default; 81 | $layout-text-color: nth($palette-page, 9) !default; 82 | 83 | // Header 84 | $layout-header-bg-color: nth($palette-primary, 6) !default; 85 | $layout-header-text-color: nth($palette-page, 3) !default; 86 | 87 | // Tabs 88 | $layout-header-tab-highlight: nth($palette-secondary, 4) !default; 89 | 90 | /* ========== Content Tabs ========== */ 91 | 92 | $tab-highlight-color: nth($palette-primary, 6) !default; 93 | $tab-text-color: rgba(0, 0, 0, 0.6) !default; 94 | $tab-active-text-color: black !default; 95 | $tab-border-color: nth($palette-grey, 4) !default; 96 | 97 | /* ========== Checkboxes ========== */ 98 | 99 | $checkbox-color: nth($palette-primary, 6) !default; 100 | $checkbox-off-color: rgba(0, 0, 0, 0.54) !default; 101 | $checkbox-disabled-color: rgba(0, 0, 0, 0.26) !default; 102 | 103 | /* ========== Switches ========== */ 104 | 105 | $switch-color: nth($palette-primary, 6) !default; 106 | $switch-thumb-color: $switch-color !default; 107 | $switch-track-color: rgba($switch-color, 0.5) !default; 108 | 109 | $switch-off-thumb-color: nth($palette-grey, 1) !default; 110 | $switch-off-track-color: rgba(0, 0, 0, 0.26) !default; 111 | $switch-disabled-thumb-color: nth($palette-grey, 5) !default; 112 | $switch-disabled-track-color: rgba(0, 0, 0, 0.12) !default; 113 | 114 | /* ========== Spinner ========== */ 115 | 116 | $spinner-color-1: #4285f4 !default; 117 | $spinner-color-2: #db4437 !default; 118 | $spinner-color-3: #f4b400 !default; 119 | $spinner-color-4: #0f9d58 !default; 120 | 121 | $spinner-single-color: nth($palette-primary, 6) !default; 122 | 123 | /* ========== Text fields ========== */ 124 | 125 | $input-text-background-color: transparent !default; 126 | $input-text-label-color: rgba(0, 0, 0, 0.26) !default; 127 | $input-text-bottom-border-color: rgba(0, 0, 0, 0.12) !default; 128 | $input-text-highlight-color: nth($palette-primary, 6) !default; 129 | $input-text-disabled-color: $input-text-bottom-border-color !default; 130 | $input-text-error-color: nth($palette-red, 7) !default; 131 | 132 | /* ========== Card ========== */ 133 | 134 | $card-background-color: white !default; 135 | $heading-color: white !default; 136 | $heading-back-color: white !default; 137 | $heading-back-bgcolor: nth($palette-primary, 8) !default; 138 | $action-link-color: nth($palette-primary, 10) !default; 139 | $caption-color: white !default; 140 | $caption-bgcolor: nth($palette-primary, 10) !default; 141 | $image-placeholder-color: nth($palette-accent, 5) !default; 142 | $lower-text-color: nth($palette-primary, 10) !default; 143 | 144 | /* Heading back */ 145 | $heading-back-border: 0 none rgb(255, 255, 255) !default; 146 | 147 | /* ========== Sliders ========== */ 148 | 149 | $range-bg-color: rgba(0, 0, 0, 0.26) !default; 150 | $range-color: nth($palette-primary, 6) !default; 151 | 152 | /* ========== List ========== */ 153 | 154 | $list-container-item-border-color: #ddd !default; 155 | $list-container-view-background: rgba(255,255,255,1) !default; 156 | 157 | /* ========== Item ========== */ 158 | 159 | // Default Item Colors 160 | $default-item-text-color: #000; 161 | $default-item-outline-color: nth($palette-secondary, 5) !default; 162 | $default-item-hover-bg-color: nth($palette-secondary, 3) !default; 163 | $default-item-focus-bg-color: nth($palette-secondary, 3) !default; 164 | $default-item-active-bg-color: nth($palette-secondary, 4) !default; 165 | 166 | // Disabled Button Colors 167 | $disabled-item-text-color: nth($palette-disabled, 5) !default; 168 | 169 | /* ========== Dropdown menu ========== */ 170 | 171 | $default-dropdown-bg-color: #fff !default; 172 | 173 | /* ========== Tooltips ========== */ 174 | 175 | $tooltip-text-color: #fff !default; 176 | $tooltip-background-color: nth($palette-accent, 7) !default; 177 | 178 | /* ========== Footer ========== */ 179 | 180 | $footer-bg-color: #454545 !default; 181 | $footer-color: #A0A0A0 !default; 182 | $footer-heading-color: #E2E2E2 !default; 183 | $footer-button-fill-color: $footer-color !default; 184 | $footer-underline-color: $footer-color; 185 | -------------------------------------------------------------------------------- /app/styles/animation/_animation.scss: -------------------------------------------------------------------------------- 1 | $animation-curve-fast-out-slow-in: cubic-bezier(0.4, 0, 0.2, 1) !default; 2 | $animation-curve-linear-out-slow-in: cubic-bezier(0, 0, 0.2, 1) !default; 3 | $animation-curve-fast-out-linear-in: cubic-bezier(0.4, 0, 1, 1) !default; 4 | 5 | $animation-curve-default: $animation-curve-fast-out-slow-in !default; 6 | 7 | @mixin material-animation-fast-out-slow-in($duration:0.2s) { 8 | transition-duration: $duration; 9 | transition-timing-function: $animation-curve-fast-out-slow-in; 10 | } 11 | 12 | @mixin material-animation-linear-out-slow-in($duration:0.2s) { 13 | transition-duration: $duration; 14 | transition-timing-function: $animation-curve-linear-out-slow-in; 15 | } 16 | 17 | @mixin material-animation-fast-out-linear-in($duration:0.2s) { 18 | transition-duration: $duration; 19 | transition-timing-function: $animation-curve-fast-out-linear-in; 20 | } 21 | 22 | @mixin material-animation-default($duration:0.2s) { 23 | transition-duration: $duration; 24 | transition-timing-function: $animation-curve-default; 25 | } 26 | -------------------------------------------------------------------------------- /app/styles/button/_button.scss: -------------------------------------------------------------------------------- 1 | @import "../typography/typography"; 2 | @import "../colors"; 3 | @import "../shadow/shadow"; 4 | @import "../animation/animation"; 5 | @import "../ripple/ripple"; 6 | 7 | 8 | /** 9 | * 10 | * Dimensions 11 | * 12 | */ 13 | $button-min-width: 64px !default; 14 | $button-height: 36px !default; 15 | $button-padding: 8px !default; 16 | $button-margin: 4px !default; 17 | $button-border-radius: 2px !default; 18 | 19 | $button-fab-size: 56px !default; 20 | $button-fab-size-mini: 40px !default; 21 | $button-fab-font-size: 24px !default; 22 | 23 | $button-icon-size: 32px !default; 24 | 25 | // Focus shadow mixin. 26 | @mixin focus-shadow() { 27 | box-shadow: 0 0 8px rgba(0,0,0,.18),0 8px 16px rgba(0,0,0,.36); 28 | } 29 | 30 | 31 | // The button component. Defaults to a flat button. 32 | .wsk-button { 33 | background: transparent; 34 | border: none; 35 | border-radius: $button-border-radius; 36 | color: $button-secondary-color; 37 | display: block; 38 | position: relative; 39 | height: $button-height; 40 | min-width: $button-min-width; 41 | padding: 0 $button-padding; 42 | margin: 0 $button-margin; 43 | display: inline-block; 44 | @include typo-button(); 45 | overflow: hidden; 46 | will-change: box-shadow, transform; 47 | transition: box-shadow 0.2s $animation-curve-fast-out-linear-in, 48 | background-color 0.2s $animation-curve-default, 49 | color 0.2s $animation-curve-default; 50 | outline: none; 51 | cursor: pointer; 52 | 53 | &::-moz-focus-inner { 54 | border: 0; 55 | } 56 | 57 | &:hover { 58 | background-color: $button-hover-color; 59 | } 60 | 61 | &:focus:not(:active) { 62 | background-color: $button-focus-color; 63 | } 64 | 65 | &:active { 66 | background-color: $button-active-color; 67 | } 68 | 69 | // Bump up specificity by using [disabled] twice. 70 | &[disabled][disabled] { 71 | color: $button-secondary-color-disabled; 72 | cursor: auto; 73 | background-color: transparent; 74 | } 75 | 76 | &.wsk-button--colored { 77 | color: $button-primary-color-alt; 78 | 79 | &:focus:not(:active) { 80 | background-color: $button-focus-color-alt; 81 | } 82 | } 83 | } 84 | 85 | 86 | // Raised buttons 87 | .wsk-button--raised { 88 | background: $button-primary-color; 89 | @include shadow-z1(); 90 | 91 | &:active { 92 | @include shadow-z3(); 93 | background-color: $button-active-color; 94 | } 95 | 96 | &:focus:not(:active) { 97 | @include focus-shadow(); 98 | background-color: $button-active-color; 99 | } 100 | 101 | &.wsk-button--colored { 102 | background: $button-primary-color-alt; 103 | color: $button-secondary-color-alt; 104 | 105 | &:hover { 106 | background-color: $button-hover-color-alt; 107 | } 108 | 109 | &:active { 110 | background-color: $button-active-color-alt; 111 | } 112 | 113 | &:focus:not(:active) { 114 | background-color: $button-active-color-alt; 115 | } 116 | 117 | & .wsk-ripple { 118 | background: $button-ripple-color-alt; 119 | } 120 | } 121 | 122 | // Bump up specificity by using [disabled] twice. 123 | &[disabled][disabled] { 124 | background-color: $button-primary-color-disabled; 125 | color: $button-secondary-color-disabled; 126 | @include shadow-z1(); 127 | } 128 | } 129 | 130 | 131 | // FABs 132 | .wsk-button--fab { 133 | border-radius: 50%; 134 | font-size: $button-fab-font-size; 135 | height: $button-fab-size; 136 | margin: auto; 137 | min-width: $button-fab-size; 138 | width: $button-fab-size; 139 | padding: 0; 140 | overflow: hidden; 141 | background: $button-primary-color; 142 | @include shadow-z1(); 143 | 144 | &.wsk-button--mini-fab { 145 | height: $button-fab-size-mini; 146 | min-width: $button-fab-size-mini; 147 | width: $button-fab-size-mini; 148 | } 149 | 150 | & .wsk-button__ripple-container { 151 | border-radius: 50%; 152 | // Fixes clipping bug in Safari. 153 | -webkit-mask-image: -webkit-radial-gradient(circle, white, black); 154 | } 155 | 156 | &:active { 157 | @include shadow-z3(); 158 | background-color: $button-active-color; 159 | } 160 | 161 | &:focus:not(:active) { 162 | @include focus-shadow(); 163 | background-color: $button-active-color; 164 | } 165 | 166 | &.wsk-button--colored { 167 | background: $button-fab-color-alt; 168 | color: $button-secondary-color-alt; 169 | 170 | &:hover { 171 | background-color: $button-fab-hover-color-alt; 172 | } 173 | 174 | &:focus:not(:active) { 175 | background-color: $button-fab-active-color-alt; 176 | } 177 | 178 | &:active { 179 | background-color: $button-fab-active-color-alt; 180 | } 181 | 182 | & .wsk-ripple { 183 | background: $button-ripple-color-alt; 184 | } 185 | } 186 | 187 | // Bump up specificity by using [disabled] twice. 188 | &[disabled][disabled] { 189 | background-color: $button-primary-color-disabled; 190 | color: $button-secondary-color-disabled; 191 | @include shadow-z1(); 192 | } 193 | } 194 | 195 | 196 | // Icon buttons 197 | .wsk-button--icon { 198 | border-radius: 50%; 199 | font-size: $button-fab-font-size; 200 | height: $button-icon-size; 201 | margin-left: 0; 202 | margin-right: 0; 203 | min-width: $button-icon-size; 204 | width: $button-icon-size; 205 | padding: 0; 206 | overflow: hidden; 207 | 208 | & .wsk-button__ripple-container { 209 | border-radius: 50%; 210 | // Fixes clipping bug in Safari. 211 | -webkit-mask-image: -webkit-radial-gradient(circle, white, black); 212 | } 213 | } 214 | 215 | 216 | // Ripples 217 | .wsk-button__ripple-container { 218 | display: block; 219 | height: 100%; 220 | left: 0px; 221 | position: absolute; 222 | top: 0px; 223 | width: 100%; 224 | z-index: 0; 225 | overflow: hidden; 226 | 227 | .wsk-button[disabled] & .wsk-ripple { 228 | background-color: transparent; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /app/styles/card/_card.scss: -------------------------------------------------------------------------------- 1 | @import "../typography/typography"; 2 | @import "../colors"; 3 | @import "../shadow/shadow"; 4 | 5 | /* Card dimensions */ 6 | $card-width: 330px !default; 7 | $card-height: 508px !default; 8 | $card-font-size: 16px !default; 9 | $card-heading-font-size: 24px !default; 10 | $card-heading-height: 96px !default; 11 | $card-horizontal-padding: 16px !default; 12 | $card-vertical-padding: 8px !default; 13 | 14 | $card-heading-perspective-origin-x: 165px !default; 15 | $card-heading-perspective-origin-y: 56px !default; 16 | 17 | $card-heading-transform-origin-x: 165px !default; 18 | $card-heading-transform-origin-y: 56px !default; 19 | 20 | $card-heading-text-transform-origin-x: 149px !default; 21 | $card-heading-text-transform-origin-y: 48px !default; 22 | 23 | $card-caption-perspective-origin-x: 165px !default; 24 | $card-caption-perspective-origin-y: 16px !default; 25 | 26 | $card-caption-font-size: 12px !default; 27 | $card-caption-height: 16px !default; 28 | 29 | $card-lower-font-size: 13px !default; 30 | $card-lower-line-height: 18px !default; 31 | $card-lower-height: 108px !default; 32 | $card-lower-vertical-margin: 10px !default; 33 | 34 | $card-bottom-height: 50px !default; 35 | $card-bottom-font-size: 16px !default; 36 | 37 | $card-bottom-anchor-height: 50px !default; 38 | $card-bottom-anchor-line-height: 50px !default; 39 | $card-bottom-anchor-font-size: 13px !default; 40 | 41 | $card-bottom-anchor-perspective-origin-x: 165px !default; 42 | $card-bottom-anchor-perspective-origin-y: 25px !default; 43 | 44 | $card-bottom-anchor-transform-origin-x: 165px !default; 45 | $card-bottom-anchor-transform-origin-y: 25px !default; 46 | 47 | $card-heading-text-font-weight: 300 !default; 48 | $card-z-index: 1 !default; 49 | 50 | /* Cover image */ 51 | $cover-image-height: 186px !default; 52 | $background-image-url: '' !default; 53 | 54 | .wsk-card { 55 | font-size: $card-font-size; 56 | height : $card-height; 57 | overflow : hidden; 58 | width : $card-width; 59 | z-index : $card-z-index; 60 | position : relative; 61 | background: $card-background-color; 62 | } 63 | 64 | .wsk-card--img-container { 65 | background-color : $image-placeholder-color; 66 | @if str_length($background-image-url) > 0 { 67 | background-image : url($background-image-url); 68 | } 69 | background-repeat : repeat; 70 | background-position : 50% 50%; 71 | background-size : cover; 72 | background-origin : padding-box; 73 | background-attachment: scroll; 74 | height : $cover-image-height; 75 | } 76 | 77 | .wsk-card--heading { 78 | align-items : center; 79 | background : $heading-back-bgcolor none repeat scroll 0 0 / auto padding-box border-box; 80 | border : $heading-back-border; 81 | color : $heading-back-color; 82 | display : flex; 83 | font-size : $card-heading-font-size; 84 | height : $card-heading-height; 85 | justify-items : stretch; 86 | line-height : normal; 87 | // TODO: Does this need to be anything other than none? 88 | outline : rgb(255, 255, 255) none 0; 89 | padding : $card-vertical-padding $card-horizontal-padding; 90 | perspective-origin: $card-heading-perspective-origin-x $card-heading-perspective-origin-y; 91 | text-decoration : none solid rgb(255, 255, 255); 92 | transform-origin : $card-heading-transform-origin-x $card-heading-transform-origin-y; 93 | width : 100%; 94 | } 95 | 96 | .wsk-card--heading-text { 97 | align-self : center; 98 | // TODO: Does this need to be anything other than none? 99 | border : 0 none rgb(255, 255, 255); 100 | color : $heading-color; 101 | display : flex; 102 | font-size : inherit; 103 | font-weight : $card-heading-text-font-weight; 104 | height : $card-heading-height; 105 | justify-self : stretch; 106 | line-height : normal; 107 | // TODO: Does this need to be anything other than none? 108 | outline : rgb(255, 255, 255) none 0; 109 | overflow : hidden; 110 | // TODO: Does this need to be anything other than none? 111 | text-decoration : none solid rgb(255, 255, 255); 112 | transform-origin: $card-heading-text-transform-origin-x $card-heading-text-transform-origin-y; 113 | width : 90%; 114 | } 115 | 116 | .wsk-card--caption { 117 | background : $caption-bgcolor none repeat scroll 0 0 / auto padding-box border-box; 118 | // TODO: Does this need to be anything other than none? 119 | border : 0 none rgb(255, 255, 255); 120 | color : $caption-color; 121 | font-size : $card-caption-font-size; 122 | height : $card-caption-height; 123 | line-height : normal; 124 | // TODO: Does this need to be anything other than none? 125 | outline : rgb(255, 255, 255) none 0; 126 | padding : $card-vertical-padding $card-horizontal-padding; 127 | perspective-origin: $card-caption-perspective-origin-x $card-caption-perspective-origin-y; 128 | // TODO: Does this need to be anything other than none? 129 | text-decoration : none solid rgb(255, 255, 255); 130 | width : 100%; 131 | } 132 | 133 | .wsk-card--lower { 134 | // TODO: Does this need to be anything other than none? 135 | border : 0 none rgb(68, 68, 68); 136 | color : $lower-text-color; 137 | font-size : $card-lower-font-size; 138 | height : $card-lower-height; 139 | line-height : $card-lower-line-height; 140 | margin : $card-lower-vertical-margin 0; 141 | // TODO: Does this need to be anything other than none? 142 | outline : rgb(68, 68, 68) none 0; 143 | overflow : hidden; 144 | padding : 0 $card-horizontal-padding; 145 | // TODO: Does this need to be anything other than none? 146 | text-decoration: none solid rgb(68, 68, 68); 147 | width : 90%; 148 | } 149 | 150 | .wsk-card--bottom { 151 | font-size : $card-bottom-font-size; 152 | height : $card-bottom-height; 153 | line-height: normal; 154 | width : 100%; 155 | } 156 | 157 | .wsk-card--bottom a { 158 | // TODO: Does this need to be anything other than none? 159 | border-bottom : 0 none rgb(48, 63, 159); 160 | // TODO: Does this need to be anything other than none? 161 | border-left : 0 none rgb(48, 63, 159); 162 | // TODO: Does this need to be anything other than none? 163 | border-right : 0 none rgb(48, 63, 159); 164 | border-top : 1px solid rgb(235, 235, 235); 165 | box-sizing : border-box; 166 | color : $action-link-color; 167 | cursor : pointer; 168 | display : inline-block; 169 | font-size : $card-bottom-anchor-font-size; 170 | font-weight : normal; 171 | height : $card-bottom-anchor-height; 172 | line-height : $card-bottom-anchor-line-height; 173 | // TODO: Does this need to be anything other than none? 174 | outline : rgb(48, 63, 159) none 0; 175 | padding : 0 $card-horizontal-padding; 176 | perspective-origin: $card-bottom-anchor-perspective-origin-x $card-bottom-anchor-perspective-origin-y; 177 | text-decoration : none solid rgb(48, 63, 159); 178 | text-transform : uppercase; 179 | transform-origin : $card-bottom-anchor-transform-origin-x $card-bottom-anchor-transform-origin-y; 180 | width : 100%; 181 | } 182 | -------------------------------------------------------------------------------- /app/styles/main.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Visual Style Guide styles 3 | * Remove if you prefer to use a CSS library, like Bootstrap 4 | */ 5 | 6 | @import "layout/layout"; 7 | @import "card/card"; 8 | @import "shadow/shadow"; 9 | @import "button/button"; 10 | 11 | body { 12 | margin: 0px; 13 | } 14 | 15 | * { 16 | user-select: none; 17 | } 18 | 19 | main { 20 | padding: 20px; 21 | } 22 | 23 | html, body { 24 | color: $text-color-secondary; 25 | height: 100%; 26 | } 27 | 28 | h1, h2, h3, h4, h5 { 29 | color: $text-color-primary; 30 | } 31 | 32 | .wsk-layout__content { 33 | 34 | .Camera { 35 | 36 | &.Camera--facing-user { 37 | .Camera-display { 38 | transform: scale(-1, 1); 39 | } 40 | } 41 | 42 | &.Camera--facing-environment { 43 | 44 | } 45 | 46 | .Camera-video { 47 | display:none; 48 | } 49 | 50 | .Camera-toggle { 51 | position: absolute; 52 | margin: 1em; 53 | bottom: 0; 54 | right: 0; 55 | background-color: #0288D1; 56 | z-index: 50; 57 | } 58 | 59 | .Camera-toggle-input { 60 | display: inline-block; 61 | position: absolute; 62 | top: 0; 63 | bottom: -100px; 64 | } 65 | 66 | .Camera-toggle-input + label { 67 | position: absolute; 68 | top: 0; 69 | left: 0; 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | width: 100%; 74 | height: 100%; 75 | background-color: #0288D1; 76 | outline: none; 77 | -webkit-tap-highlight-color: transparent; 78 | } 79 | 80 | .Camera-toggle-input + label .front { 81 | display:inline-block; 82 | } 83 | 84 | .Camera-toggle-input + label .rear { 85 | display: none; 86 | } 87 | 88 | .Camera-toggle-input:checked + label .front { 89 | display: none !important; 90 | } 91 | 92 | .Camera-toggle-input:checked + label .rear { 93 | display: inline-block !important; 94 | } 95 | 96 | .Camera-display { 97 | position: absolute; 98 | width: 100%; 99 | height: 100%; 100 | pointer-events: none; 101 | } 102 | 103 | .Camera-overlay { 104 | transition: all 300ms ease-in-out; 105 | position: absolute; 106 | top: 0; 107 | left: 0; 108 | bottom: 0; 109 | right: 0; 110 | background-color: transparent; 111 | border-style: solid; 112 | border-color: rgba(0, 0, 0, 0.5); 113 | pointer-events: none; 114 | z-index: 20; 115 | } 116 | 117 | .Camera-overlay:before { 118 | content:''; 119 | top: 0; 120 | left: 0; 121 | position: absolute; 122 | border-top: solid rgba(255,255,255, 0.7) 2px; 123 | border-left: solid rgba(255,255,255, 0.7) 2px; 124 | width: 10%; 125 | height: 10%; 126 | } 127 | 128 | .Camera-overlay:after { 129 | content:''; 130 | right: 0; 131 | bottom: 0; 132 | position: absolute; 133 | border-bottom: solid rgba(255,255,255, 0.7) 2px; 134 | border-right: solid rgba(255,255,255, 0.7) 2px; 135 | width: 10%; 136 | height: 10%; 137 | } 138 | 139 | .Camera-instructions { 140 | position: absolute; 141 | bottom: 0; 142 | background-color: #323232; 143 | font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif; 144 | color: white; 145 | padding: 18px; 146 | border-radius: 2px; 147 | margin: 1em; 148 | } 149 | } 150 | 151 | .QRCodeSuccessDialog { 152 | height: auto !important; 153 | display: none; 154 | position: absolute !important; 155 | margin: 2em auto; 156 | width: 80%; 157 | top: 0; 158 | left: 0; 159 | right: 0; 160 | z-index: 100; 161 | } 162 | } -------------------------------------------------------------------------------- /app/styles/palette/_palette.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Material design color palettes. 4 | * @see http://www.google.com/design/spec/style/color.html 5 | * 6 | **/ 7 | 8 | /* ========== Color Palettes ========== */ 9 | 10 | // Color order: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, A100, A200, 11 | // A400, A700. 12 | 13 | $palette-red: #ffebee #ffcdd2 #ef9a9a #e57373 #ef5350 #f44336 #e53935 14 | #d32f2f #c62828 #b71c1c #ff8a80 #ff5252 #ff1744 #d50000; 15 | 16 | $palette-pink: #fce4ec #f8bbd0 #f8bbd0 #f06292 #ec407a #e91e63 #d81b60 17 | #c2185b #ad1457 #880e4f #ff80ab #ff4081 #f50057 #c51162; 18 | 19 | $palette-purple: #f3e5f5 #e1bee7 #ce93d8 #ba68c8 #ab47bc #9c27b0 #8e24aa 20 | #7b1fa2 #6a1b9a #4a148c #ea80fc #e040fb #d500f9 #aa00ff; 21 | 22 | $palette-deep-purple: #ede7f6 #d1c4e9 #b39ddb #9575cd #7e57c2 #673ab7 23 | #5e35b1 #512da8 #4527a0 #311b92 #b388ff #7c4dff #651fff 24 | #6200ea; 25 | 26 | $palette-indigo: #e8eaf6 #c5cae9 #9fa8da #7986cb #5c6bc0 #3f51b5 #3949ab 27 | #303f9f #283593 #1a237e #8c9eff #536dfe #3d5afe #304ffe; 28 | 29 | $palette-blue: #e3f2fd #bbdefb #90caf9 #64b5f6 #42a5f5 #2196f3 #1e88e5 30 | #1976d2 #1565c0 #0d47a1 #82b1ff #448aff #2979ff #2962ff; 31 | 32 | $palette-light-blue: #e1f5fe #b3e5fc #81d4fa #4fc3f7 #29b6f6 #03a9f4 #039be5 33 | #0288d1 #0277bd #01579b #80d8ff #40c4ff #00b0ff #0091ea; 34 | 35 | $palette-cyan: #e0f7fa #b2ebf2 #80deea #4dd0e1 #26c6da #00bcd4 #00acc1 36 | #0097a7 #00838f #006064 #84ffff #18ffff #00e5ff #00b8d4; 37 | 38 | $palette-teal: #e0f2f1 #b2dfdb #80cbc4 #4db6ac #26a69a #009688 #00897b 39 | #00796b #00695c #004d40 #a7ffeb #64ffda #1de9b6 #00bfa5; 40 | 41 | $palette-green: #e8f5e9 #c8e6c9 #a5d6a7 #81c784 #66bb6a #4caf50 #43a047 42 | #388e3c #2e7d32 #1b5e20 #b9f6ca #69f0ae #00e676 #00c853; 43 | 44 | $palette-light-green: #f1f8e9 #dcedc8 #c5e1a5 #aed581 #9ccc65 #8bc34a #7cb342 45 | #689f38 #558b2f #33691e #ccff90 #b2ff59 #76ff03 #64dd17; 46 | 47 | $palette-lime: #f9fbe7 #f0f4c3 #e6ee9c #dce775 #d4e157 #cddc39 #c0ca33 48 | #afb42b #9e9d24 #827717 #f4ff81 #eeff41 #c6ff00 #aeea00; 49 | 50 | $palette-yellow: #fffde7 #fff9c4 #fff59d #fff176 #ffee58 #ffeb3b #fdd835 51 | #fbc02d #f9a825 #f57f17 #ffff8d #ffff00 #ffea00 #ffd600; 52 | 53 | $palette-amber: #fff8e1 #ffecb3 #ffe082 #ffd54f #ffca28 #ffc107 #ffb300 54 | #ffa000 #ff8f00 #ff6f00 #ffe57f #ffd740 #ffc400 #ffab00; 55 | 56 | $palette-orange: #fff3e0 #ffe0b2 #ffcc80 #ffb74d #ffa726 #ff9800 #fb8c00 57 | #f57c00 #ef6c00 #e65100 #ffd180 #ffab40 #ff9100 #ff6d00; 58 | 59 | $palette-deep-orange: #fbe9e7 #ffccbc #ffab91 #ff8a65 #ff7043 #ff5722 #f4511e 60 | #e64a19 #d84315 #bf360c #ff9e80 #ff6e40 #ff3d00 #dd2c00; 61 | 62 | 63 | // Color order: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900. 64 | 65 | $palette-brown: #efebe9 #d7ccc8 #bcaaa4 #a1887f #8d6e63 66 | #795548 #6d4c41 #5d4037 #4e342e #3e2723; 67 | 68 | $palette-grey: #fafafa #f5f5f5 #eeeeee #e0e0e0 #bdbdbd 69 | #9e9e9e #757575 #616161 #424242 #212121; 70 | 71 | $palette-blue-grey: #eceff1 #cfd8dc #b0bec5 #90a4ae #78909c 72 | #607d8b #546e7a #455a64 #37474f #263238; 73 | -------------------------------------------------------------------------------- /app/styles/ripple/_ripple.scss: -------------------------------------------------------------------------------- 1 | @import "../palette/palette"; 2 | @import "../animation/animation"; 3 | 4 | 5 | .wsk-ripple { 6 | background : $ripple-bg-color; 7 | border-radius : 50%; 8 | height : 50px; 9 | left : 0; 10 | opacity : 0; 11 | pointer-events : none; 12 | position : absolute; 13 | top : 0; 14 | transform : translate(-50%, -50%); 15 | width : 50px; 16 | overflow : hidden; 17 | 18 | &.is-animating { 19 | transition: transform 0.6s $animation-curve-linear-out-slow-in, 20 | width 0.6s $animation-curve-linear-out-slow-in, 21 | height 0.6s $animation-curve-linear-out-slow-in, 22 | opacity 0.6s $animation-curve-linear-out-slow-in; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/styles/shadow/_shadow.scss: -------------------------------------------------------------------------------- 1 | @mixin shadow-z1() { 2 | box-shadow: 0 1px 1.5px 0 rgba(0,0,0,0.12), 0 1px 1px 0 rgba(0,0,0,0.24); 3 | } 4 | @mixin shadow-z2() { 5 | box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 5px 0 rgba(0,0,0,0.23); 6 | } 7 | @mixin shadow-z3() { 8 | box-shadow: 0 10px 10px 0 rgba(0,0,0,0.19), 0 6px 3px 0 rgba(0,0,0,0.23); 9 | } 10 | @mixin shadow-z4() { 11 | box-shadow: 0 14px 14px 0 rgba(0,0,0,0.25), 0 10px 5px 0 rgba(0,0,0,0.22); 12 | } 13 | @mixin shadow-z5() { 14 | box-shadow: 0 19px 19px 0 rgba(0,0,0,0.30), 0 15px 6px 0 rgba(0,0,0,0.22); 15 | } 16 | 17 | .wsk-shadow--z1 { 18 | @include shadow-z1(); 19 | } 20 | 21 | .wsk-shadow--z2 { 22 | @include shadow-z2(); 23 | } 24 | 25 | .wsk-shadow--z3 { 26 | @include shadow-z3(); 27 | } 28 | 29 | .wsk-shadow--z4 { 30 | @include shadow-z4(); 31 | } 32 | 33 | .wsk-shadow--z5 { 34 | @include shadow-z5(); 35 | } 36 | -------------------------------------------------------------------------------- /app/styles/typography/_typography.scss: -------------------------------------------------------------------------------- 1 | /* We're splitting fonts into "preferred" and "performance" in order to optimize 2 | page loading. For important text, such as the body, we want it to load 3 | immediately and not wait for the web font load, whereas for other sections, 4 | such as headers and titles, we're OK with things taking a bit longer to load. 5 | We do have some optional classes and parameters in the mixins, in case you 6 | definitely want to make sure you're using the preferred font and don't mind 7 | the performance hit. 8 | We should be able to improve on this once CSS Font Loading L3 becomes more 9 | widely available. 10 | */ 11 | $preferred_font: 'Roboto', 'Helvetica', 'Arial', sans-serif; 12 | $performance_font: 'Helvetica', 'Arial', sans-serif; 13 | 14 | @mixin typo-preferred-font($usePreferred: true) { 15 | @if $usePreferred { 16 | font-family: $preferred_font; 17 | } 18 | } 19 | 20 | @mixin typo-display-4($colorContrast: false, $usePreferred: true) { 21 | @include typo-preferred-font($usePreferred); 22 | font-size: 112px; 23 | font-weight: 300; 24 | line-height: 1; 25 | letter-spacing: -0.04em; 26 | 27 | @if $colorContrast { 28 | opacity: 0.54; 29 | } 30 | } 31 | 32 | @mixin typo-display-3($colorContrast: false, $usePreferred: true) { 33 | @include typo-preferred-font($usePreferred); 34 | font-size: 56px; 35 | font-weight: 400; 36 | line-height: 1; 37 | letter-spacing: -0.02em; 38 | 39 | @if $colorContrast { 40 | opacity: 0.54; 41 | } 42 | } 43 | 44 | @mixin typo-display-2($colorContrast: false, $usePreferred: true) { 45 | @include typo-preferred-font($usePreferred); 46 | font-size: 45px; 47 | font-weight: 400; 48 | line-height: 48px; 49 | 50 | @if $colorContrast { 51 | opacity: 0.54; 52 | } 53 | } 54 | 55 | @mixin typo-display-1($colorContrast: false, $usePreferred: true) { 56 | @include typo-preferred-font($usePreferred); 57 | font-size: 34px; 58 | font-weight: 400; 59 | line-height: 40px; 60 | 61 | @if $colorContrast { 62 | opacity: 0.54; 63 | } 64 | } 65 | 66 | @mixin typo-headline($colorContrast: false, $usePreferred: true) { 67 | @include typo-preferred-font($usePreferred); 68 | font-size: 24px; 69 | font-weight: 400; 70 | line-height: 32px; 71 | -moz-osx-font-smoothing: grayscale; 72 | 73 | @if $colorContrast { 74 | opacity: 0.87; 75 | } 76 | } 77 | 78 | @mixin typo-title($colorContrast: false, $usePreferred: true) { 79 | @include typo-preferred-font($usePreferred); 80 | font-size: 20px; 81 | font-weight: 500; 82 | line-height: 1; 83 | letter-spacing: 0.02em; 84 | 85 | @if $colorContrast { 86 | opacity: 0.87; 87 | } 88 | } 89 | 90 | @mixin typo-subhead($colorContrast: false, $usePreferred: true) { 91 | @include typo-preferred-font($usePreferred); 92 | font-size: 16px; 93 | font-weight: 400; 94 | line-height: 24px; 95 | letter-spacing: 0.04em; 96 | 97 | @if $colorContrast { 98 | opacity: 0.87; 99 | } 100 | } 101 | 102 | @mixin typo-subhead-2($colorContrast: false, $usePreferred: true) { 103 | @include typo-preferred-font($usePreferred); 104 | font-size: 16px; 105 | font-weight: 400; 106 | line-height: 28px; 107 | letter-spacing: 0.04em; 108 | 109 | @if $colorContrast { 110 | opacity: 0.87; 111 | } 112 | } 113 | 114 | @mixin typo-body-2($colorContrast: false, $usePreferred: false) { 115 | @include typo-preferred-font($usePreferred); 116 | font-size: 14px; 117 | @if $usePreferred { 118 | font-weight: 500; 119 | } @else { 120 | font-weight: bold; 121 | } 122 | line-height: 24px; 123 | letter-spacing: 0.04em; 124 | 125 | @if $colorContrast { 126 | opacity: 0.87; 127 | } 128 | } 129 | 130 | @mixin typo-body-1($colorContrast: false, $usePreferred: false) { 131 | @include typo-preferred-font($usePreferred); 132 | font-size: 14px; 133 | font-weight: 400; 134 | line-height: 24px; 135 | letter-spacing: 0.04em; 136 | 137 | @if $colorContrast { 138 | opacity: 0.87; 139 | } 140 | } 141 | 142 | @mixin typo-caption($colorContrast: false, $usePreferred: false) { 143 | @include typo-preferred-font($usePreferred); 144 | font-size: 12px; 145 | font-weight: 400; 146 | line-height: 1; 147 | letter-spacing: 0.08em; 148 | 149 | @if $colorContrast { 150 | opacity: 0.54; 151 | } 152 | } 153 | 154 | @mixin typo-blockquote($colorContrast: false, $usePreferred: true) { 155 | @include typo-preferred-font($usePreferred); 156 | font-size: 24px; 157 | font-weight: 300; 158 | font-style: italic; 159 | line-height: 1; 160 | letter-spacing: 0.08em; 161 | &:before { 162 | content: '“'; 163 | opacity: 0.54; 164 | } 165 | 166 | &:after { 167 | content: '”'; 168 | opacity: 0.54; 169 | } 170 | 171 | @if $colorContrast { 172 | opacity: 0.54; 173 | } 174 | } 175 | 176 | @mixin typo-menu($colorContrast: false, $usePreferred: true) { 177 | @include typo-preferred-font($usePreferred); 178 | font-size: 14px; 179 | font-weight: 500; 180 | line-height: 1; 181 | letter-spacing: 0.04em; 182 | 183 | @if $colorContrast { 184 | opacity: 0.87; 185 | } 186 | } 187 | 188 | @mixin typo-button($colorContrast: false, $usePreferred: true) { 189 | @include typo-preferred-font($usePreferred); 190 | font-size: 14px; 191 | font-weight: 400; 192 | text-transform: uppercase; 193 | line-height: 1; 194 | letter-spacing: 0.04em; 195 | 196 | @if $colorContrast { 197 | opacity: 0.87; 198 | } 199 | } 200 | 201 | html, body { 202 | font-family: $performance_font; 203 | font-size: 14px; 204 | font-weight: 400; 205 | line-height: 20px; 206 | } 207 | 208 | h1, h2, h3, h4, h5, h6, p { 209 | margin: 0; 210 | padding: 0; 211 | } 212 | 213 | /** 214 | * Styles for HTML elements 215 | */ 216 | 217 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small { 218 | @include typo-display-3($colorContrast: true); 219 | font-size: 0.6em; 220 | } 221 | 222 | h1 { 223 | @include typo-display-3(); 224 | } 225 | 226 | h2 { 227 | @include typo-display-2(); 228 | } 229 | 230 | h3 { 231 | @include typo-display-1(); 232 | } 233 | 234 | h4 { 235 | @include typo-headline(); 236 | } 237 | 238 | h5 { 239 | @include typo-title(); 240 | } 241 | 242 | h6 { 243 | @include typo-subhead(); 244 | } 245 | 246 | p { 247 | @include typo-body-1(); 248 | 249 | margin: 0 0 16px 0; 250 | } 251 | 252 | a { 253 | font-weight: bold; 254 | } 255 | 256 | blockquote { 257 | @include typo-blockquote(); 258 | } 259 | 260 | mark { 261 | background-color: #f4ff81; 262 | } 263 | 264 | dt { 265 | font-weight: 700; 266 | } 267 | 268 | /** 269 | * Class Name Styles 270 | */ 271 | 272 | .wsk-typography--display-4 { 273 | @include typo-display-4(); 274 | } 275 | 276 | .wsk-typography--display-4--colorContrast { 277 | @include typo-display-4($colorContrast: true); 278 | } 279 | 280 | .wsk-typography--display-3 { 281 | @include typo-display-3(); 282 | } 283 | 284 | .wsk-typography--display-3--colorContrast { 285 | @include typo-display-3($colorContrast: true); 286 | } 287 | 288 | .wsk-typography--display-2 { 289 | @include typo-display-2(); 290 | } 291 | 292 | .wsk-typography--display-2--colorContrast { 293 | @include typo-display-2($colorContrast: true); 294 | } 295 | 296 | .wsk-typography--display-1 { 297 | @include typo-display-1(); 298 | } 299 | 300 | .wsk-typography--display-1--colorContrast { 301 | @include typo-display-1($colorContrast: true); 302 | } 303 | 304 | .wsk-typography--headline { 305 | @include typo-headline(); 306 | } 307 | 308 | .wsk-typography--headline--colorContrast { 309 | @include typo-headline($colorContrast: true); 310 | } 311 | 312 | .wsk-typography--title { 313 | @include typo-title(); 314 | } 315 | 316 | .wsk-typography--title--colorContrast { 317 | @include typo-title($colorContrast: true); 318 | } 319 | 320 | .wsk-typography--subhead { 321 | @include typo-subhead(); 322 | } 323 | 324 | .wsk-typography--subhead--colorContrast { 325 | @include typo-subhead($colorContrast: true); 326 | } 327 | 328 | .wsk-typography--body-2 { 329 | @include typo-body-2(); 330 | } 331 | 332 | .wsk-typography--Body-2--colorContrast { 333 | @include typo-body-2($colorContrast: true); 334 | } 335 | 336 | .wsk-typography--body-1 { 337 | @include typo-body-1(); 338 | } 339 | 340 | .wsk-typography--body-1--colorContrast { 341 | @include typo-body-1($colorContrast: true); 342 | } 343 | 344 | .wsk-typography--body-2--forcePreferredFont { 345 | @include typo-body-2($usePreferred: true); 346 | } 347 | 348 | .wsk-typography--Body-2--forcePreferredFont--colorContrast { 349 | @include typo-body-2($colorContrast: true, $usePreferred: true); 350 | } 351 | 352 | .wsk-typography--body-1--forcePreferredFont { 353 | @include typo-body-1($usePreferred: true); 354 | } 355 | 356 | .wsk-typography--body-1--forcePreferredFont--colorContrast { 357 | @include typo-body-1($colorContrast: true, $usePreferred: true); 358 | } 359 | 360 | .wsk-typography--caption { 361 | @include typo-caption(); 362 | } 363 | 364 | .wsk-typography--caption--forcePreferredFont { 365 | @include typo-caption($usePreferred: true); 366 | } 367 | 368 | .wsk-typography--caption--colorContrast { 369 | @include typo-caption($colorContrast: true); 370 | } 371 | 372 | .wsk-typography--caption--forcePreferredFont--colorContrast { 373 | @include typo-caption($colorContrast: true, $usePreferred: true); 374 | } 375 | 376 | .wsk-typography--menu { 377 | @include typo-menu(); 378 | } 379 | 380 | .wsk-typography--menu--colorContrast { 381 | @include typo-menu($colorContrast: true); 382 | } 383 | 384 | .wsk-typography--button { 385 | @include typo-button(); 386 | } 387 | 388 | .wsk-typography--button--colorContrast { 389 | @include typo-button($colorContrast: true); 390 | } 391 | 392 | .wsk-typography--textLeft { 393 | text-align: left 394 | } 395 | 396 | .wsk-typography--textRight { 397 | text-align: right 398 | } 399 | 400 | .wsk-typography--textCenter { 401 | text-align: center 402 | } 403 | 404 | .wsk-typography--textJustify { 405 | text-align: justify 406 | } 407 | 408 | .wsk-typography--textNowrap { 409 | white-space: nowrap 410 | } 411 | 412 | 413 | .wsk-typography--textLowercase { 414 | text-transform: lowercase 415 | } 416 | 417 | .wsk-typography--textUppercase { 418 | text-transform: uppercase 419 | } 420 | 421 | .wsk-typography--textCapitalize { 422 | text-transform: capitalize 423 | } 424 | 425 | .wsk-typography--tableStriped > tbody > tr:nth-child(odd) > td, 426 | .wsk-typography--tableStriped > tbody > tr:nth-child(odd) > th { 427 | background-color: #f9f9f9 428 | } 429 | 430 | .wsk-typography--tableStriped > tbody > tr:nth-child(odd) > td, 431 | .wsk-typography--tableStriped > tbody > tr:nth-child(odd) > th { 432 | background-color: #f9f9f9 433 | } 434 | -------------------------------------------------------------------------------- /app/sw.js: -------------------------------------------------------------------------------- 1 | // Version 0.15 2 | 3 | importScripts('/cache-polyfill.js'); 4 | 5 | self.addEventListener('install', function(e) { 6 | e.waitUntil( 7 | caches.open('qrsnapper').then(function(cache) { 8 | return cache.addAll([ 9 | '/', 10 | '/cache-polyfill.js', 11 | '/images/ic_camera_front_24px.svg', 12 | '/images/ic_camera_rear_24px.svg', 13 | '/styles/main.css', 14 | '/scripts/main.min.js', 15 | '/scripts/jsqrcode/qrworker.js', 16 | '/scripts/jsqrcode/grid.js', 17 | '/scripts/jsqrcode/version.js', 18 | '/scripts/jsqrcode/detector.js', 19 | '/scripts/jsqrcode/formatinf.js', 20 | '/scripts/jsqrcode/errorlevel.js', 21 | '/scripts/jsqrcode/bitmat.js', 22 | '/scripts/jsqrcode/datablock.js', 23 | '/scripts/jsqrcode/bmparser.js', 24 | '/scripts/jsqrcode/datamask.js', 25 | '/scripts/jsqrcode/rsdecoder.js', 26 | '/scripts/jsqrcode/gf256poly.js', 27 | '/scripts/jsqrcode/gf256.js', 28 | '/scripts/jsqrcode/decoder.js', 29 | '/scripts/jsqrcode/qrcode.js', 30 | '/scripts/jsqrcode/findpat.js', 31 | '/scripts/jsqrcode/alignpat.js', 32 | '/scripts/jsqrcode/databr.js'] 33 | ); 34 | }) 35 | ); 36 | }); 37 | 38 | self.addEventListener('fetch', function(event) { 39 | var url = event.request.url; 40 | 41 | if(url !== "https://www.google-analytics.com/analytics.js") { 42 | event.respondWith( 43 | caches.match(event.request).then(function(response) { 44 | return response || fetch(event.request.url); 45 | }) 46 | ); 47 | } 48 | 49 | }); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Web Starter Kit 4 | * Copyright 2015 Google Inc. All rights reserved. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License 17 | * 18 | */ 19 | 20 | 'use strict'; 21 | 22 | // Include Gulp & Tools We'll Use 23 | var gulp = require('gulp'); 24 | var $ = require('gulp-load-plugins')(); 25 | var del = require('del'); 26 | var runSequence = require('run-sequence'); 27 | var browserSync = require('browser-sync'); 28 | var pagespeed = require('psi'); 29 | var reload = browserSync.reload; 30 | 31 | // Lint JavaScript 32 | gulp.task('jshint', function() { 33 | return gulp.src(['app/scripts/**/*.js', 'app/styleguide/**/*.js']) 34 | .pipe(reload({stream: true, once: true})) 35 | .pipe($.jshint()) 36 | .pipe($.jshint.reporter('jshint-stylish')) 37 | .pipe($.if(!browserSync.active, $.jshint.reporter('fail'))); 38 | }); 39 | 40 | // Optimize Images 41 | gulp.task('images', function() { 42 | return gulp.src('app/images/**/*') 43 | .pipe(gulp.dest('dist/images')) 44 | .pipe($.size({title: 'images'})); 45 | }); 46 | 47 | // Copy All Files At The Root Level (app) 48 | gulp.task('copy', function() { 49 | return gulp.src([ 50 | 'app/*', 51 | '!app/*.html', 52 | 'node_modules/apache-server-configs/dist/.htaccess' 53 | ], { 54 | dot: true 55 | }).pipe(gulp.dest('dist')) 56 | .pipe($.size({title: 'copy'})); 57 | }); 58 | 59 | // Copy All Filescopy-workerscripts At The Root Level (app) 60 | gulp.task('copy-workerscripts', function() { 61 | return gulp.src('app/scripts/jsqrcode/*.js') 62 | .pipe(gulp.dest('dist/scripts/jsqrcode/')) 63 | .pipe($.size({title: 'copy-workerscripts'})); 64 | }); 65 | 66 | // Copy image files from the Styleguide 67 | gulp.task('styleguide-images', function() { 68 | return gulp.src('app/styleguide/**/*.{svg,png,jpg}') 69 | .pipe(gulp.dest('dist/styleguide/')) 70 | .pipe($.size({title: 'styleguide-images'})); 71 | }); 72 | 73 | // Copy Web Fonts To Dist 74 | gulp.task('fonts', function() { 75 | return gulp.src(['app/fonts/**']) 76 | .pipe(gulp.dest('dist/fonts')) 77 | .pipe($.size({title: 'fonts'})); 78 | }); 79 | 80 | // Compile and Automatically Prefix Stylesheets 81 | gulp.task('styles', function() { 82 | 83 | var AUTOPREFIXER_BROWSERS = [ 84 | 'ie >= 10', 85 | 'ie_mob >= 10', 86 | 'ff >= 30', 87 | 'chrome >= 34', 88 | 'safari >= 7', 89 | 'opera >= 23', 90 | 'ios >= 7', 91 | 'android >= 4.4', 92 | 'bb >= 10' 93 | ]; 94 | 95 | // For best performance, don't add Sass partials to `gulp.src` 96 | return gulp.src([ 97 | 'app/**/*.scss', 98 | 'app/styles/**/*.css' 99 | ]) 100 | .pipe($.changed('styles', {extension: '.scss'})) 101 | .pipe($.sass({ 102 | precision: 10, 103 | onError: console.error.bind(console, 'Sass error:') 104 | })) 105 | .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS)) 106 | .pipe(gulp.dest('.tmp')) 107 | // Concatenate And Minify Styles 108 | .pipe($.if('*.css', $.csso())) 109 | .pipe(gulp.dest('dist')) 110 | .pipe($.size({title: 'styles'})); 111 | }); 112 | 113 | // Concatenate And Minify JavaScript 114 | gulp.task('scripts', function() { 115 | var sources = ['app/scripts/*.js', 116 | 'app/styleguide/wskComponentHandler.js', 'app/styleguide/**/*.js']; 117 | return gulp.src(sources) 118 | .pipe($.concat('main.js')) 119 | // .pipe($.uglify({preserveComments: 'some'})) 120 | // Output Files 121 | .pipe(gulp.dest('dist/scripts')) 122 | .pipe($.size({title: 'scripts'})); 123 | }); 124 | 125 | // Scan Your HTML For Assets & Optimize Them 126 | gulp.task('html', function() { 127 | var assets = $.useref.assets({searchPath: '{.tmp,app}'}); 128 | 129 | return gulp.src('app/**/**/*.html') 130 | .pipe(assets) 131 | // Remove Any Unused CSS 132 | // Note: If not using the Style Guide, you can delete it from 133 | // the next line to only include styles your project uses. 134 | .pipe($.if('*.css', $.uncss({ 135 | html: [ 136 | 'app/index.html', 137 | 'app/styleguide.html' 138 | ], 139 | // CSS Selectors for UnCSS to ignore 140 | ignore: [] 141 | }))) 142 | 143 | // Concatenate And Minify Styles 144 | // In case you are still using useref build blocks 145 | .pipe($.if('*.css', $.csso())) 146 | .pipe(assets.restore()) 147 | .pipe($.useref()) 148 | // Minify Any HTML 149 | .pipe($.if('*.html', $.minifyHtml())) 150 | // Output Files 151 | .pipe(gulp.dest('dist')) 152 | .pipe($.size({title: 'html'})); 153 | }); 154 | 155 | // Clean Output Directory 156 | gulp.task('clean', del.bind(null, ['.tmp', 'dist/*', '!dist/.git'], {dot: true})); 157 | 158 | // Watch Files For Changes & Reload 159 | gulp.task('serve', ['styles'], function() { 160 | browserSync({ 161 | notify: false, 162 | // Customize the BrowserSync console logging prefix 163 | logPrefix: 'WSK', 164 | // Run as an https by uncommenting 'https: true' 165 | // Note: this uses an unsigned certificate which on first access 166 | // will present a certificate warning in the browser. 167 | // https: true, 168 | server: ['.tmp', 'app'] 169 | }); 170 | 171 | gulp.watch(['app/**/**/**/*.html'], reload); 172 | gulp.watch(['app/**/**/**/*.{scss,css}'], ['styles', reload]); 173 | gulp.watch(['app/scripts/**/*.js','app/styleguide/**/*.js'], ['jshint']); 174 | gulp.watch(['app/images/**/*'], reload); 175 | }); 176 | 177 | // Build and serve the output from the dist build 178 | gulp.task('serve:dist', ['default'], function() { 179 | browserSync({ 180 | notify: false, 181 | logPrefix: 'WSK', 182 | // Run as an https by uncommenting 'https: true' 183 | // Note: this uses an unsigned certificate which on first access 184 | // will present a certificate warning in the browser. 185 | // https: true, 186 | server: 'dist', 187 | baseDir: "dist" 188 | }); 189 | }); 190 | 191 | // Build Production Files, the Default Task 192 | gulp.task('default', ['clean'], function(cb) { 193 | runSequence('styles', ['html', 'scripts', 'images', 'styleguide-images', 'fonts', 'copy', 'copy-workerscripts'], cb); 194 | }); 195 | 196 | // Run PageSpeed Insights 197 | // Update `url` below to the public URL for your site 198 | gulp.task('pagespeed', pagespeed.bind(null, { 199 | // By default, we use the PageSpeed Insights 200 | // free (no API key) tier. You can use a Google 201 | // Developer API key if you have one. See 202 | // http://goo.gl/RkN0vE for info key: 'YOUR_API_KEY' 203 | url: 'https://example.com', 204 | strategy: 'mobile' 205 | })); 206 | 207 | // Load custom tasks from the `tasks` directory 208 | // try { require('require-dir')('tasks'); } catch (err) { console.error(err); } 209 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "apache-server-configs": "^2.7.1", 4 | "browser-sync": "^1.3.0", 5 | "del": "^1.1.1", 6 | "gulp": "^3.8.5", 7 | "gulp-autoprefixer": "^2.0.0", 8 | "gulp-cache": "^0.2.4", 9 | "gulp-changed": "^1.0.0", 10 | "gulp-concat": "^2.4.1", 11 | "gulp-csso": "^0.2.9", 12 | "gulp-flatten": "0.0.4", 13 | "gulp-if": "^1.2.1", 14 | "gulp-jshint": "^1.6.3", 15 | "gulp-load-plugins": "^0.8.0", 16 | "gulp-minify-html": "^0.1.8", 17 | "gulp-replace": "^0.5.0", 18 | "gulp-sass": "^2.1.1", 19 | "gulp-size": "^1.0.0", 20 | "gulp-uglify": "^1.0.1", 21 | "gulp-uncss": "^0.5.2", 22 | "gulp-useref": "^1.0.1", 23 | "jshint-stylish": "^1.0.0", 24 | "material-design-icons": "^1.0.1", 25 | "opn": "^1.0.0", 26 | "psi": "^0.1.6", 27 | "require-dir": "^0.1.0", 28 | "run-sequence": "^1.0.2" 29 | }, 30 | "engines": { 31 | "node": ">=0.10.0" 32 | }, 33 | "private": true, 34 | "scripts": { 35 | "test": "gulp && git status | grep 'working directory clean' >/dev/null || (echo 'Please commit all changes generated by building'; exit 1)" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /qrcode-essentials.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/qrcode-essentials.zip -------------------------------------------------------------------------------- /qrsnapper.p12.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/qrcode/1cf8758d2ba488e21d72ad852a9c4f12f564c49e/qrsnapper.p12.enc --------------------------------------------------------------------------------