├── .editorconfig ├── .github ├── CONTRIBUTING.md └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── LICENSE ├── TODO.md ├── demo ├── basic.html └── events.html ├── dist ├── collection │ ├── collection-manifest.json │ ├── components │ │ └── konami-listener │ │ │ ├── konami-listener.css │ │ │ └── konami-listener.js │ ├── index.js │ ├── interface.js │ └── main.css ├── esm │ ├── es5 │ │ ├── konami-listener.components.js │ │ ├── konami-listener.core.js │ │ ├── konami-listener.define.js │ │ ├── konami-listener.global.js │ │ ├── konami-listener.js │ │ ├── konami-listener.sc.js │ │ └── polyfills │ │ │ ├── array.js │ │ │ ├── dom.js │ │ │ ├── fetch.js │ │ │ ├── object.js │ │ │ ├── promise.js │ │ │ └── string.js │ └── index.js ├── index.js ├── konami-listener.js ├── konami-listener │ ├── es5-build-disabled.js │ ├── konami-listener.core.js │ ├── konami-listener.global.js │ ├── konami-listener.js │ └── konami-listener.sc.js └── types │ ├── components.d.ts │ ├── components │ └── konami-listener │ │ └── konami-listener.d.ts │ ├── index.d.ts │ └── stencil.core.d.ts ├── package-lock.json ├── package.json ├── readme.md ├── src ├── components.d.ts ├── components │ └── konami-listener │ │ ├── konami-listener.css │ │ ├── konami-listener.spec.ts │ │ └── konami-listener.tsx ├── index.d.ts ├── index.html ├── index.ts └── main.css ├── stencil.config.ts ├── tsconfig.json └── www ├── build ├── konami-listener.js └── konami-listener │ ├── es5-build-disabled.js │ ├── konami-listener.core.js │ ├── konami-listener.global.js │ ├── konami-listener.js │ ├── konami-listener.registry.json │ └── konami-listener.sc.js ├── index.html └── main.css /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for your interest in contributing to the Stencil Component Starter! :tada: 4 | 5 | 6 | ## Contributing Etiquette 7 | 8 | Please see our [Contributor Code of Conduct](https://github.com/ionic-team/stencil/blob/master/CODE_OF_CONDUCT.md) for information on our rules of conduct. 9 | 10 | 11 | ## Creating an Issue 12 | 13 | * If you have a question about using Stencil, please ask in the [Stencil Worldwide Slack](https://join.slack.com/t/stencil-worldwide/shared_invite/enQtMjQ2MzkyMTY0MTk0LTQ4ODgzYjFjNjdkNDY3YWVhMmNlMTljMWQxNTM3Yjg0ZTIyZTM1MmU2YWE5YzNjNzE1MmQ3ZTk2NjQ1YzM5ZDM group. 14 | 15 | * It is required that you clearly describe the steps necessary to reproduce the issue you are running into. Although we would love to help our users as much as possible, diagnosing issues without clear reproduction steps is extremely time-consuming and simply not sustainable. 16 | 17 | * The issue list of this repository is exclusively for bug reports and feature requests. Non-conforming issues will be closed immediately. 18 | 19 | * Issues with no clear steps to reproduce will not be triaged. If an issue is labeled with "needs reply" and receives no further replies from the author of the issue for more than 5 days, it will be closed. 20 | 21 | * If you think you have found a bug, or have a new feature idea, please start by making sure it hasn't already been [reported](https://github.com/ionic-team/stencil/issues?utf8=%E2%9C%93&q=is%3Aissue). You can search through existing issues to see if there is a similar one reported. Include closed issues as it may have been closed with a solution. 22 | 23 | * Next, [create a new issue](https://github.com/ionic-team/stencil-component-starter/issues/new) that thoroughly explains the problem. Please fill out the populated issue form before submitting the issue. 24 | 25 | 26 | ## Creating a Pull Request 27 | 28 | * We appreciate you taking the time to contribute! Before submitting a pull request, we ask that you please [create an issue](#creating-an-issue) that explains the bug or feature request and let us know that you plan on creating a pull request for it. If an issue already exists, please comment on that issue letting us know you would like to submit a pull request for it. This helps us to keep track of the pull request and make sure there isn't duplicated effort. 29 | 30 | * Looking for an issue to fix? Make sure to look through our issues with the [help wanted](https://github.com/ionic-team/stencil-component-starter/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) label! 31 | 32 | ### Setup 33 | 34 | 1. Fork the repo. 35 | 2. Clone your fork. 36 | 3. Make a branch for your change. 37 | 4. Run `npm install` (make sure you have [node](https://nodejs.org/en/) and [npm](http://blog.npmjs.org/post/85484771375/how-to-install-npm) installed first) 38 | 39 | 40 | #### Updates 41 | 42 | 1. Unit test. Unit test. Unit test. Please take a look at how other unit tests are written, and you can't write too many tests. 43 | 2. If there is a `*.spec.ts` file located in the components folder, update it to include a test for your change, if needed. If this file doesn't exist, please notify us. 44 | 3. Run `npm run test` or `npm run test.watch` to make sure all tests are working, regardless if a test was added. 45 | 46 | 47 | ## Commit Message Format 48 | 49 | We have very precise rules over how our git commit messages should be formatted. This leads to readable messages that are easy to follow when looking through the project history. We also use the git commit messages to generate our changelog. (Ok you got us, it's basically Angular's commit message format). 50 | 51 | `type(scope): subject` 52 | 53 | #### Type 54 | Must be one of the following: 55 | 56 | * **feat**: A new feature 57 | * **fix**: A bug fix 58 | * **docs**: Documentation only changes 59 | * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) 60 | * **refactor**: A code change that neither fixes a bug nor adds a feature 61 | * **perf**: A code change that improves performance 62 | * **test**: Adding missing tests 63 | * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation 64 | 65 | #### Scope 66 | The scope can be anything specifying place of the commit change. For example `renderer`, `compiler`, etc. 67 | 68 | #### Subject 69 | The subject contains succinct description of the change: 70 | 71 | * use the imperative, present tense: "change" not "changed" nor "changes" 72 | * do not capitalize first letter 73 | * do not place a period `.` at the end 74 | * entire length of the commit message must not go over 50 characters 75 | * describe what the commit does, not what issue it relates to or fixes 76 | * **be brief, yet descriptive** - we should have a good understanding of what the commit does by reading the subject 77 | 78 | 79 | #### Adding Documentation 80 | 81 | Please see the [stencil-site](https://github.com/ionic-team/stencil-site) repo to update documentation. 82 | 83 | 84 | ## License 85 | 86 | By contributing your code to the ionic-team/stencil GitHub Repository, you agree to license your contribution under the MIT license. 87 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **Resources:** 2 | Before submitting an issue, please consult our [docs](https://stenciljs.com/). 3 | 4 | **Stencil version:** (run `npm list @stencil/core` from a terminal/cmd prompt and paste output below): 5 | 6 | ``` 7 | insert the output from npm list @stencil/core here 8 | ``` 9 | 10 | **I'm submitting a ...** (check one with "x") 11 | [ ] bug report 12 | [ ] feature request 13 | [ ] support request => Please do not submit support requests here, use one of these channels: https://forum.ionicframework.com/ or https://stencil-worldwide.slack.com 14 | 15 | **Current behavior:** 16 | 17 | 18 | **Expected behavior:** 19 | 20 | 21 | **Steps to reproduce:** 22 | 24 | 25 | **Related code:** 26 | 27 | ``` 28 | insert any relevant code here 29 | ``` 30 | 31 | **Other information:** 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.sw[mnpcod] 3 | *.log 4 | *.lock 5 | *.tmp 6 | *.tmp.* 7 | log.txt 8 | *.sublime-project 9 | *.sublime-workspace 10 | 11 | .idea/ 12 | .vscode/ 13 | .sass-cache/ 14 | .versions/ 15 | node_modules/ 16 | $RECYCLE.BIN/ 17 | 18 | .DS_Store 19 | Thumbs.db 20 | UserInterfaceState.xcuserstate 21 | .env 22 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natemoo-re/konami-listener/a83012a8d2544cae6e8fc1179259cb78085a0bf1/.npmignore -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ionic 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - [ ] Write tests 4 | - [x] Publish to NPM 5 | - [ ] Enable interaction on touch devices (without using Hammer) -------------------------------------------------------------------------------- /demo/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Stencil Component Starter 7 | 8 | 9 | 10 | 11 | 12 | 13 |

↑ + ↑ + ↓ + ↓ + ← + → + ← + → + B + A

14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Stencil Component Starter 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /dist/collection/collection-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": [ 3 | { 4 | "tag": "konami-listener", 5 | "dependencies": [], 6 | "componentClass": "KonamiListener", 7 | "componentPath": "components/konami-listener/konami-listener.js", 8 | "styles": { 9 | "$": { 10 | "stylePaths": [ 11 | "components/konami-listener/konami-listener.css" 12 | ] 13 | } 14 | }, 15 | "states": [ 16 | { 17 | "name": "inputs" 18 | } 19 | ], 20 | "listeners": [ 21 | { 22 | "event": "document:keydown", 23 | "method": "handleKey", 24 | "passive": false, 25 | "capture": false 26 | } 27 | ], 28 | "hostElement": { 29 | "name": "el" 30 | }, 31 | "events": [ 32 | { 33 | "event": "input" 34 | }, 35 | { 36 | "event": "match" 37 | } 38 | ], 39 | "shadow": true 40 | } 41 | ], 42 | "collections": [], 43 | "compiler": { 44 | "name": "@stencil/core", 45 | "version": "0.11.4", 46 | "typescriptVersion": "2.9.2" 47 | }, 48 | "bundles": [] 49 | } -------------------------------------------------------------------------------- /dist/collection/components/konami-listener/konami-listener.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: none; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | bottom: 0; 7 | right: 0; 8 | } 9 | 10 | :host(.active) { 11 | display: block; 12 | } 13 | -------------------------------------------------------------------------------- /dist/collection/components/konami-listener/konami-listener.js: -------------------------------------------------------------------------------- 1 | export class KonamiListener { 2 | constructor() { 3 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 4 | this.accepted = [...new Set(this.keys)]; 5 | this.inputs = []; 6 | } 7 | handleKey(e) { 8 | let { key } = e; 9 | if (!this.accepted.includes(key)) 10 | return; 11 | (this.keys[this.inputs.length] === key) 12 | ? this.handleInput(key) 13 | : this.reset(); 14 | } 15 | handleInput(key) { 16 | this.inputs.push(key); 17 | this.input.emit({ key, index: this.inputs.length - 1 }); 18 | if (this.inputs.length === 10) { 19 | this.handleMatch(); 20 | } 21 | } 22 | handleMatch() { 23 | this.match.emit(); 24 | this.el.classList.add('active'); 25 | this.reset(); 26 | } 27 | reset() { 28 | if (this.inputs.length) { 29 | this.inputs = []; 30 | this.input.emit({ reset: true }); 31 | } 32 | } 33 | render() { 34 | return h("slot", null); 35 | } 36 | static get is() { return "konami-listener"; } 37 | static get encapsulation() { return "shadow"; } 38 | static get properties() { return { 39 | "el": { 40 | "elementRef": true 41 | }, 42 | "inputs": { 43 | "state": true 44 | } 45 | }; } 46 | static get events() { return [{ 47 | "name": "input", 48 | "method": "input", 49 | "bubbles": true, 50 | "cancelable": true, 51 | "composed": true 52 | }, { 53 | "name": "match", 54 | "method": "match", 55 | "bubbles": true, 56 | "cancelable": true, 57 | "composed": true 58 | }]; } 59 | static get listeners() { return [{ 60 | "name": "document:keydown", 61 | "method": "handleKey" 62 | }]; } 63 | static get style() { return "/**style-placeholder:konami-listener:**/"; } 64 | } 65 | -------------------------------------------------------------------------------- /dist/collection/index.js: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | -------------------------------------------------------------------------------- /dist/collection/interface.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natemoo-re/konami-listener/a83012a8d2544cae6e8fc1179259cb78085a0bf1/dist/collection/interface.js -------------------------------------------------------------------------------- /dist/collection/main.css: -------------------------------------------------------------------------------- 1 | @keyframes fade { 2 | 0% { 3 | opacity: 0.5; 4 | } 5 | 100% { 6 | opacity: 0; 7 | } 8 | } 9 | 10 | :root { 11 | --color-red: #f4511e; 12 | --header-size: 200px; 13 | --footer-size: 56px; 14 | } 15 | 16 | html, body { 17 | padding: 0; 18 | margin: 0; 19 | } 20 | 21 | header { 22 | box-sizing: border-box; 23 | width: 100%; 24 | height: var(--header-size); 25 | display: flex; 26 | flex-flow: column nowrap; 27 | align-items: center; 28 | justify-content: center; 29 | text-align: center; 30 | padding: 0 12px; 31 | max-width: 570px; 32 | margin: 0 auto; 33 | } 34 | header h1 { 35 | margin: 0; 36 | font-family: 'Press Start 2P', sans-serif; 37 | line-height: 1.2; 38 | } 39 | header p { 40 | font-family: sans-serif; 41 | color: rgba(0,0,0,0.54); 42 | } 43 | header code { 44 | box-sizing: border-box; 45 | padding: 1px 4px 0 4px; 46 | line-height: 1; 47 | background-color: rgba(0, 0, 0, 0.12); 48 | border-radius: 4px; 49 | white-space: nowrap; 50 | } 51 | 52 | 53 | footer { 54 | box-sizing: border-box; 55 | height: var(--footer-size); 56 | background-color: red; 57 | width: 100%; 58 | display: flex; 59 | align-items: center; 60 | } 61 | footer .inner { 62 | box-sizing: border-box; 63 | width: 100%; 64 | display: flex; 65 | align-items: center; 66 | justify-content: space-between; 67 | max-width: 570px; 68 | margin: 0 auto; 69 | } 70 | footer .inner div { 71 | box-sizing: border-box; 72 | text-align: center; 73 | padding: 0 12px; 74 | } 75 | footer h4 { 76 | margin: 0; 77 | font-family: sans-serif; 78 | font-weight: 400; 79 | color: #FFF; 80 | } 81 | footer a { 82 | font-family: 'Press Start 2P', sans-serif; 83 | font-size: 14px; 84 | color: #FFF; 85 | } 86 | 87 | main, .prize { 88 | height: calc(calc(100vh - var(--header-size)) - var(--footer-size)); 89 | width: 100%; 90 | max-width: calc(1000px / 4); 91 | 92 | display: flex; 93 | align-items: center; 94 | justify-content: center; 95 | margin: 0 auto; 96 | } 97 | 98 | main.hidden { 99 | opacity: 0; 100 | } 101 | 102 | .prize { 103 | flex-flow: column nowrap; 104 | align-items: center; 105 | justify-content: center; 106 | height: 100vh; 107 | font-size: 126px; 108 | } 109 | .prize h2 { 110 | font-size: 32px; 111 | font-family: 'Press Start 2P', sans-serif; 112 | color: var(--color-red); 113 | margin: 0; 114 | line-height: 1; 115 | 116 | } 117 | 118 | .inputs { 119 | width: 100%; 120 | flex-flow: row wrap; 121 | display: flex; 122 | align-items: center; 123 | justify-content: space-between; 124 | transform: scale(.7); 125 | } 126 | 127 | .inputs > div { 128 | --size: 100px; 129 | margin: 12px; 130 | width: var(--size); 131 | height: var(--size); 132 | } 133 | 134 | .inputs > div > svg > path.fixed { 135 | fill: rgba(0,0,0,0.32); 136 | /* stroke-width: 4px; */ 137 | z-index: 1; 138 | } 139 | .inputs > div > svg > path:not(.fixed) { 140 | transition: 250ms ease-out all; 141 | fill: none; 142 | stroke-width: 2px; 143 | opacity: 0.5; 144 | z-index: -1; 145 | stroke: var(--color-red); 146 | 147 | opacity: 0; 148 | transform-origin: center center; 149 | } 150 | 151 | .inputs > div > svg { 152 | overflow: visible; 153 | } 154 | 155 | .inputs > div.active > svg > path.fixed { 156 | fill: var(--color-red); 157 | stroke: transparent; 158 | } 159 | .inputs > div.active > svg > path:not(.fixed) { 160 | display: block; 161 | opacity: 1; 162 | transform: scale(1.5); 163 | animation: 150ms 100ms fade forwards; 164 | } 165 | 166 | div.up.active > svg > path:not(.fixed) { 167 | transform: translateY(-70px); 168 | } 169 | div.down.active > svg > path:not(.fixed) { 170 | transform: translateY(70px); 171 | } 172 | div.left.active > svg > path:not(.fixed) { 173 | transform: translateX(-70px); 174 | } 175 | div.right.active > svg > path:not(.fixed) { 176 | transform: translateX(70px); 177 | } 178 | div.B.active > svg > path:not(.fixed) { 179 | transform: scale(1.75); 180 | } 181 | div.A.active > svg > path:not(.fixed) { 182 | transform: scale(1.75); 183 | } 184 | @media only screen and (min-width: 555px) { 185 | header { 186 | margin-bottom: calc(var(--header-size) / -4); 187 | } 188 | main { 189 | padding-bottom: calc(var(--header-size) / 4); 190 | } 191 | } 192 | 193 | @media only screen and (min-width: 1180px) { 194 | main { 195 | max-width: 1180px; 196 | } 197 | footer { 198 | justify-content: center; 199 | } 200 | .inputs { 201 | transform: scale(1); 202 | } 203 | 204 | .inputs > div { 205 | margin: 0; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /dist/esm/es5/konami-listener.components.js: -------------------------------------------------------------------------------- 1 | // KonamiListener: Host Data, ES Module/ES5 Target 2 | 3 | export var KonamiListener = ["konami-listener",function(o){return(o.scoped?import("./konami-listener.sc.js"):import("./konami-listener.js")).then(function(m){return m.KonamiListener})},1,[["el",7],["inputs",5]],1,[["document:keydown","handleKey"]]]; -------------------------------------------------------------------------------- /dist/esm/es5/konami-listener.define.js: -------------------------------------------------------------------------------- 1 | // KonamiListener: Custom Elements Define Library, ES Module/ES5 Target 2 | import { defineCustomElement } from './konami-listener.core.js'; 3 | import { 4 | KonamiListener 5 | } from './konami-listener.components.js'; 6 | 7 | export function defineCustomElements(window, opts) { 8 | defineCustomElement(window, [ 9 | KonamiListener 10 | ], opts); 11 | } -------------------------------------------------------------------------------- /dist/esm/es5/konami-listener.global.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | export default function appGlobal(namespace, Context, window, document, resourcesUrl, hydratedCssClass) { 3 | } -------------------------------------------------------------------------------- /dist/esm/es5/konami-listener.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | import { h } from './konami-listener.core.js'; 3 | var KonamiListener = /** @class */ (function () { 4 | function KonamiListener() { 5 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 6 | this.accepted = new Set(this.keys).slice(); 7 | this.inputs = []; 8 | } 9 | KonamiListener.prototype.handleKey = function (e) { 10 | var key = e.key; 11 | if (!this.accepted.includes(key)) 12 | return; 13 | (this.keys[this.inputs.length] === key) 14 | ? this.handleInput(key) 15 | : this.reset(); 16 | }; 17 | KonamiListener.prototype.handleInput = function (key) { 18 | this.inputs.push(key); 19 | this.input.emit({ key: key, index: this.inputs.length - 1 }); 20 | if (this.inputs.length === 10) { 21 | this.handleMatch(); 22 | } 23 | }; 24 | KonamiListener.prototype.handleMatch = function () { 25 | this.match.emit(); 26 | this.el.classList.add('active'); 27 | this.reset(); 28 | }; 29 | KonamiListener.prototype.reset = function () { 30 | if (this.inputs.length) { 31 | this.inputs = []; 32 | this.input.emit({ reset: true }); 33 | } 34 | }; 35 | KonamiListener.prototype.render = function () { 36 | return h("slot", null); 37 | }; 38 | Object.defineProperty(KonamiListener, "is", { 39 | get: function () { return "konami-listener"; }, 40 | enumerable: true, 41 | configurable: true 42 | }); 43 | Object.defineProperty(KonamiListener, "encapsulation", { 44 | get: function () { return "shadow"; }, 45 | enumerable: true, 46 | configurable: true 47 | }); 48 | Object.defineProperty(KonamiListener, "properties", { 49 | get: function () { 50 | return { 51 | "el": { 52 | "elementRef": true 53 | }, 54 | "inputs": { 55 | "state": true 56 | } 57 | }; 58 | }, 59 | enumerable: true, 60 | configurable: true 61 | }); 62 | Object.defineProperty(KonamiListener, "events", { 63 | get: function () { 64 | return [{ 65 | "name": "input", 66 | "method": "input", 67 | "bubbles": true, 68 | "cancelable": true, 69 | "composed": true 70 | }, { 71 | "name": "match", 72 | "method": "match", 73 | "bubbles": true, 74 | "cancelable": true, 75 | "composed": true 76 | }]; 77 | }, 78 | enumerable: true, 79 | configurable: true 80 | }); 81 | Object.defineProperty(KonamiListener, "listeners", { 82 | get: function () { 83 | return [{ 84 | "name": "document:keydown", 85 | "method": "handleKey" 86 | }]; 87 | }, 88 | enumerable: true, 89 | configurable: true 90 | }); 91 | Object.defineProperty(KonamiListener, "style", { 92 | get: function () { return ":host {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n:host(.active) {\n display: block;\n}"; }, 93 | enumerable: true, 94 | configurable: true 95 | }); 96 | return KonamiListener; 97 | }()); 98 | export { KonamiListener }; 99 | -------------------------------------------------------------------------------- /dist/esm/es5/konami-listener.sc.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | import { h } from './konami-listener.core.js'; 3 | var KonamiListener = /** @class */ (function () { 4 | function KonamiListener() { 5 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 6 | this.accepted = new Set(this.keys).slice(); 7 | this.inputs = []; 8 | } 9 | KonamiListener.prototype.handleKey = function (e) { 10 | var key = e.key; 11 | if (!this.accepted.includes(key)) 12 | return; 13 | (this.keys[this.inputs.length] === key) 14 | ? this.handleInput(key) 15 | : this.reset(); 16 | }; 17 | KonamiListener.prototype.handleInput = function (key) { 18 | this.inputs.push(key); 19 | this.input.emit({ key: key, index: this.inputs.length - 1 }); 20 | if (this.inputs.length === 10) { 21 | this.handleMatch(); 22 | } 23 | }; 24 | KonamiListener.prototype.handleMatch = function () { 25 | this.match.emit(); 26 | this.el.classList.add('active'); 27 | this.reset(); 28 | }; 29 | KonamiListener.prototype.reset = function () { 30 | if (this.inputs.length) { 31 | this.inputs = []; 32 | this.input.emit({ reset: true }); 33 | } 34 | }; 35 | KonamiListener.prototype.render = function () { 36 | return h("slot", null); 37 | }; 38 | Object.defineProperty(KonamiListener, "is", { 39 | get: function () { return "konami-listener"; }, 40 | enumerable: true, 41 | configurable: true 42 | }); 43 | Object.defineProperty(KonamiListener, "encapsulation", { 44 | get: function () { return "shadow"; }, 45 | enumerable: true, 46 | configurable: true 47 | }); 48 | Object.defineProperty(KonamiListener, "properties", { 49 | get: function () { 50 | return { 51 | "el": { 52 | "elementRef": true 53 | }, 54 | "inputs": { 55 | "state": true 56 | } 57 | }; 58 | }, 59 | enumerable: true, 60 | configurable: true 61 | }); 62 | Object.defineProperty(KonamiListener, "events", { 63 | get: function () { 64 | return [{ 65 | "name": "input", 66 | "method": "input", 67 | "bubbles": true, 68 | "cancelable": true, 69 | "composed": true 70 | }, { 71 | "name": "match", 72 | "method": "match", 73 | "bubbles": true, 74 | "cancelable": true, 75 | "composed": true 76 | }]; 77 | }, 78 | enumerable: true, 79 | configurable: true 80 | }); 81 | Object.defineProperty(KonamiListener, "listeners", { 82 | get: function () { 83 | return [{ 84 | "name": "document:keydown", 85 | "method": "handleKey" 86 | }]; 87 | }, 88 | enumerable: true, 89 | configurable: true 90 | }); 91 | Object.defineProperty(KonamiListener, "style", { 92 | get: function () { return "\n.sc-konami-listener-h {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n.active.sc-konami-listener-h {\n display: block;\n}\n"; }, 93 | enumerable: true, 94 | configurable: true 95 | }); 96 | return KonamiListener; 97 | }()); 98 | export { KonamiListener }; 99 | -------------------------------------------------------------------------------- /dist/esm/es5/polyfills/array.js: -------------------------------------------------------------------------------- 1 | export function applyPolyfill(window, document) { 2 | /*! 3 | Array.prototype.find 4 | */ 5 | Array.prototype.find||Object.defineProperty(Array.prototype,"find",{writable:!0,configurable:!0,value:function(c,e){if(null==this)throw new TypeError('"this" is null or not defined');var b=Object(this),f=b.length>>>0;if("function"!==typeof c)throw new TypeError("predicate must be a function");for(var a=0;a>>0;if(0===n)return!1;var i,o,a=0|e,u=Math.max(0<=a?a:n-Math.abs(a),0);for(;u1)&&Zt(this)}}}),ot(u,h,{value:function(e){-1<_.call(a,e)&&o[h].apply(this,arguments)}}),o[d]&&ot(u,p,{value:o[d]}),o[v]&&ot(u,g,{value:o[v]}),i&&(f[c]=i),e=e.toUpperCase(),G[e]={constructor:t,create:i?[i,et(e)]:[e]},Z.set(t,e),n[s](e.toLowerCase(),f),en(e),Y[e].r()}function Gt(e){var t=G[e.toUpperCase()];return t&&t.constructor}function Yt(e){return typeof e=="string"?e:e&&e.is||""}function Zt(e){var t=e[h],n=t?e.attributes:j,r=n.length,i;while(r--)i=n[r],t.call(e,i.name||i.nodeName,null,i.value||i.nodeValue)}function en(e){return e=e.toUpperCase(),e in Y||(Y[e]={},Y[e].p=new K(function(t){Y[e].r=t})),Y[e].p}function tn(){X&&delete e.customElements,B(e,"customElements",{configurable:!0,value:new Kt}),B(e,"CustomElementRegistry",{configurable:!0,value:Kt});for(var t=function(t){var r=e[t];if(r){e[t]=function(t){var i,s;return t||(t=this),t[W]||(Q=!0,i=G[Z.get(t.constructor)],s=V&&i.create.length===1,t=s?Reflect.construct(r,j,i.constructor):n.createElement.apply(n,i.create),t[W]=!0,Q=!1,s||Zt(t)),t},e[t].prototype=r.prototype;try{r.prototype.constructor=e[t]}catch(i){z=!0,B(r,W,{value:e[t]})}}},r=i.get(/^HTML[A-Z]*[a-z]/),o=r.length;o--;t(r[o]));n.createElement=function(e,t){var n=Yt(t);return n?gt.call(this,e,et(n)):gt.call(this,e)},St||(Tt=!0,n[s](""))}var n=e.document,r=e.Object,i=function(e){var t=/^[A-Z]+[a-z]/,n=function(e){var t=[],n;for(n in s)e.test(n)&&t.push(n);return t},i=function(e,t){t=t.toLowerCase(),t in s||(s[e]=(s[e]||[]).concat(t),s[t]=s[t.toUpperCase()]=e)},s=(r.create||r)(null),o={},u,a,f,l;for(a in e)for(l in e[a]){f=e[a][l],s[l]=f;for(u=0;u>0),u="addEventListener",a="attached",f="Callback",l="detached",c="extends",h="attributeChanged"+f,p=a+f,d="connected"+f,v="disconnected"+f,m="created"+f,g=l+f,y="ADDITION",b="MODIFICATION",w="REMOVAL",E="DOMAttrModified",S="DOMContentLoaded",x="DOMSubtreeModified",T="<",N="=",C=/^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,k=["ANNOTATION-XML","COLOR-PROFILE","FONT-FACE","FONT-FACE-SRC","FONT-FACE-URI","FONT-FACE-FORMAT","FONT-FACE-NAME","MISSING-GLYPH"],L=[],A=[],O="",M=n.documentElement,_=L.indexOf||function(e){for(var t=this.length;t--&&this[t]!==e;);return t},D=r.prototype,P=D.hasOwnProperty,H=D.isPrototypeOf,B=r.defineProperty,j=[],F=r.getOwnPropertyDescriptor,I=r.getOwnPropertyNames,q=r.getPrototypeOf,R=r.setPrototypeOf,U=!!r.__proto__,z=!1,W="__dreCEv1",X=e.customElements,V=!/^force/.test(t.type)&&!!(X&&X.define&&X.get&&X.whenDefined),$=r.create||r,J=e.Map||function(){var t=[],n=[],r;return{get:function(e){return n[_.call(t,e)]},set:function(e,i){r=_.call(t,e),r<0?n[t.push(e)-1]=i:n[r]=i}}},K=e.Promise||function(e){function i(e){n=!0;while(t.length)t.shift()(e)}var t=[],n=!1,r={"catch":function(){return r},then:function(e){return t.push(e),n&&setTimeout(i,1),r}};return e(i),r},Q=!1,G=$(null),Y=$(null),Z=new J,et=function(e){return e.toLowerCase()},tt=r.create||function sn(e){return e?(sn.prototype=e,new sn):this},nt=R||(U?function(e,t){return e.__proto__=t,e}:I&&F?function(){function e(e,t){for(var n,r=I(t),i=0,s=r.length;ithis.status;this.statusText="statusText"in b?b.statusText:"OK";this.headers=new d(b.headers);this.url=b.url||"";this._initBody(a)}if(!e.fetch){var D="Symbol"in e&&"iterator"in Symbol,m;if(m="FileReader"in e&&"Blob"in e)try{new Blob,m=!0}catch(a){m=!1}var g={searchParams:"URLSearchParams"in e,iterable:D, 16 | blob:m,formData:"FormData"in e,arrayBuffer:"ArrayBuffer"in e};if(g.arrayBuffer){var E="[object Int8Array];[object Uint8Array];[object Uint8ClampedArray];[object Int16Array];[object Uint16Array];[object Int32Array];[object Uint32Array];[object Float32Array];[object Float64Array]".split(";");var y=function(a){return a&&DataView.prototype.isPrototypeOf(a)};var z=ArrayBuffer.isView||function(a){return a&&-1this.length)a=this.length;return this.substring(a-b.length,a)===b}}); 6 | /*! 7 | String.prototype.includes 8 | */ 9 | String.prototype.includes||(String.prototype.includes=function(b,a){"number"!==typeof a&&(a=0);return a+b.length>this.length?!1:-1!==this.indexOf(b,a)}); 10 | /*! 11 | String.prototype.startsWith 12 | */ 13 | String.prototype.startsWith||Object.defineProperty(String.prototype,"startsWith",{writable:!0,configurable:!0,value:function(b,a){return this.substr(!a||0>a?0:+a,b.length)===b}}); 14 | } -------------------------------------------------------------------------------- /dist/esm/index.js: -------------------------------------------------------------------------------- 1 | // KonamiListener: ES Module 2 | export * from './es5/konami-listener.define.js'; 3 | export * from '../collection/index.js'; -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | // KonamiListener: CommonJS Main -------------------------------------------------------------------------------- /dist/konami-listener.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Built with http://stenciljs.com 3 | * 2018-08-19T13:38:16 4 | */ 5 | (function(win, doc, namespace, fsNamespace, resourcesUrl, appCore, appCoreSsr, appCorePolyfilled, hydratedCssClass, components) { 6 | 7 | function init(win, doc, namespace, fsNamespace, resourcesUrl, appCore, appCorePolyfilled, hydratedCssClass, components, HTMLElementPrototype, App, x, y, scriptElm) { 8 | // create global namespace if it doesn't already exist 9 | App = win[namespace] = win[namespace] || {}; 10 | App.components = components; 11 | y = components.filter(function (c) { return c[2]; }).map(function (c) { return c[0]; }); 12 | if (y.length) { 13 | // auto hide components until they been fully hydrated 14 | // reusing the "x" and "i" variables from the args for funzies 15 | x = doc.createElement('style'); 16 | x.innerHTML = y.join() + '{visibility:hidden}.' + hydratedCssClass + '{visibility:inherit}'; 17 | x.setAttribute('data-styles', ''); 18 | doc.head.insertBefore(x, doc.head.firstChild); 19 | } 20 | createComponentOnReadyPrototype(win, namespace, HTMLElementPrototype); 21 | resourcesUrl = resourcesUrl || App.resourcesUrl; 22 | // figure out the script element for this current script 23 | y = doc.querySelectorAll('script'); 24 | for (x = y.length - 1; x >= 0; x--) { 25 | scriptElm = y[x]; 26 | if (scriptElm.src || scriptElm.hasAttribute('data-resources-url')) { 27 | break; 28 | } 29 | } 30 | // get the resource path attribute on this script element 31 | y = scriptElm.getAttribute('data-resources-url'); 32 | if (!resourcesUrl && y) { 33 | // the script element has a data-resources-url attribute, always use that 34 | resourcesUrl = y; 35 | } 36 | if (!resourcesUrl && scriptElm.src) { 37 | // we don't have an exact resourcesUrl, so let's 38 | // figure it out relative to this script's src and app's filesystem namespace 39 | y = scriptElm.src.split('/').slice(0, -1); 40 | resourcesUrl = (y.join('/')) + (y.length ? '/' : '') + fsNamespace + '/'; 41 | } 42 | // request the core this browser needs 43 | // test for native support of custom elements and fetch 44 | // if either of those are not supported, then use the core w/ polyfills 45 | // also check if the page was build with ssr or not 46 | x = doc.createElement('script'); 47 | if (usePolyfills(win, win.location, x, 'import("")')) { 48 | // requires the es5/polyfilled core 49 | x.src = resourcesUrl + appCorePolyfilled; 50 | } 51 | else { 52 | // let's do this! 53 | x.src = resourcesUrl + appCore; 54 | x.setAttribute('type', 'module'); 55 | x.setAttribute('crossorigin', true); 56 | } 57 | x.setAttribute('data-resources-url', resourcesUrl); 58 | x.setAttribute('data-namespace', fsNamespace); 59 | doc.head.appendChild(x); 60 | } 61 | function usePolyfills(win, location, scriptElm, dynamicImportTest) { 62 | // fyi, dev mode has verbose if/return statements 63 | // but it minifies to a nice 'lil one-liner ;) 64 | if (location.search.indexOf('core=esm') > 0) { 65 | // force esm build 66 | return false; 67 | } 68 | if ((location.search.indexOf('core=es5') > 0) || 69 | (location.protocol === 'file:') || 70 | (!(win.customElements && win.customElements.define)) || 71 | (!win.fetch) || 72 | (!(win.CSS && win.CSS.supports && win.CSS.supports('color', 'var(--c)'))) || 73 | (!('noModule' in scriptElm))) { 74 | // es5 build w/ polyfills 75 | return true; 76 | } 77 | // final test to see if this browser support dynamic imports 78 | return doesNotSupportsDynamicImports(dynamicImportTest); 79 | } 80 | function doesNotSupportsDynamicImports(dynamicImportTest) { 81 | try { 82 | new Function(dynamicImportTest); 83 | return false; 84 | } 85 | catch (e) { } 86 | return true; 87 | } 88 | function createComponentOnReadyPrototype(win, namespace, HTMLElementPrototype) { 89 | (win['s-apps'] = win['s-apps'] || []).push(namespace); 90 | if (!HTMLElementPrototype.componentOnReady) { 91 | HTMLElementPrototype.componentOnReady = function componentOnReady() { 92 | /*tslint:disable*/ 93 | var elm = this; 94 | function executor(resolve) { 95 | if (elm.nodeName.indexOf('-') > 0) { 96 | // window hasn't loaded yet and there's a 97 | // good chance this is a custom element 98 | var apps = win['s-apps']; 99 | var appsReady = 0; 100 | // loop through all the app namespaces 101 | for (var i = 0; i < apps.length; i++) { 102 | // see if this app has "componentOnReady" setup 103 | if (win[apps[i]].componentOnReady) { 104 | // this app's core has loaded call its "componentOnReady" 105 | if (win[apps[i]].componentOnReady(elm, resolve)) { 106 | // this component does belong to this app and would 107 | // have fired off the resolve fn 108 | // let's stop here, we're good 109 | return; 110 | } 111 | appsReady++; 112 | } 113 | } 114 | if (appsReady < apps.length) { 115 | // not all apps are ready yet 116 | // add it to the queue to be figured out when they are 117 | (win['s-cr'] = win['s-cr'] || []).push([elm, resolve]); 118 | return; 119 | } 120 | } 121 | // not a recognized app component 122 | resolve(null); 123 | } 124 | // callback wasn't provided, let's return a promise 125 | if (win.Promise) { 126 | // use native/polyfilled promise 127 | return new win.Promise(executor); 128 | } 129 | // promise may not have been polyfilled yet 130 | return { then: executor }; 131 | }; 132 | } 133 | } 134 | 135 | 136 | init(win, doc, namespace, fsNamespace, resourcesUrl, appCore, appCoreSsr, appCorePolyfilled, hydratedCssClass, components); 137 | 138 | })(window, document, "KonamiListener","konami-listener",0,"konami-listener.core.js","es5-build-disabled.js","hydrated",[["konami-listener","konami-listener",1,[["el",7],["inputs",5]],1,[["document:keydown","handleKey"]]]],HTMLElement.prototype); -------------------------------------------------------------------------------- /dist/konami-listener/es5-build-disabled.js: -------------------------------------------------------------------------------- 1 | setTimeout(function(){ 2 | document.body.innerHTML = '

This Stencil app is disabled for this browser.

Developers:

  • ES5 builds are disabled during development to take advantage of 2x faster build times.
  • Please see the example below or our config docs if you would like to develop on a browser that does not fully support ES2017 and custom elements.
  • Note that by default, ES5 builds and polyfills are enabled during production builds.
  • When testing browsers it is recommended to always test in production mode, and ES5 builds should always be enabled during production builds.
  • This is only an experiement and if it slows down app development then we will revert this and enable ES5 builds during dev.

Enabling ES5 builds during development:

    npm run dev --es5  

For stencil-component-starter, use:

    npm start --es5  

Enabling full production builds during development:

    npm run dev --prod  

For stencil-component-starter, use:

    npm start --prod  

Current Browser\'s Support:

Current Browser:

      
'; 3 | 4 | document.getElementById('current-browser-output').textContent = window.navigator.userAgent; 5 | document.getElementById('es-modules-test').textContent = !!('noModule' in document.createElement('script')); 6 | document.getElementById('custom-elements-test').textContent = !!(window.customElements); 7 | document.getElementById('css-variables-test').textContent = !!(window.CSS && window.CSS.supports && window.CSS.supports('color', 'var(--c)')); 8 | document.getElementById('fetch-test').textContent = !!(window.fetch); 9 | }, 10) -------------------------------------------------------------------------------- /dist/konami-listener/konami-listener.global.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | (function(namespace,resourcesUrl){"use strict"; 3 | 4 | })("KonamiListener"); -------------------------------------------------------------------------------- /dist/konami-listener/konami-listener.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | const { h } = window.KonamiListener; 3 | 4 | class KonamiListener { 5 | constructor() { 6 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 7 | this.accepted = [...new Set(this.keys)]; 8 | this.inputs = []; 9 | } 10 | handleKey(e) { 11 | let { key } = e; 12 | if (!this.accepted.includes(key)) 13 | return; 14 | (this.keys[this.inputs.length] === key) 15 | ? this.handleInput(key) 16 | : this.reset(); 17 | } 18 | handleInput(key) { 19 | this.inputs.push(key); 20 | this.input.emit({ key, index: this.inputs.length - 1 }); 21 | if (this.inputs.length === 10) { 22 | this.handleMatch(); 23 | } 24 | } 25 | handleMatch() { 26 | this.match.emit(); 27 | this.el.classList.add('active'); 28 | this.reset(); 29 | } 30 | reset() { 31 | if (this.inputs.length) { 32 | this.inputs = []; 33 | this.input.emit({ reset: true }); 34 | } 35 | } 36 | render() { 37 | return h("slot", null); 38 | } 39 | static get is() { return "konami-listener"; } 40 | static get encapsulation() { return "shadow"; } 41 | static get properties() { return { 42 | "el": { 43 | "elementRef": true 44 | }, 45 | "inputs": { 46 | "state": true 47 | } 48 | }; } 49 | static get events() { return [{ 50 | "name": "input", 51 | "method": "input", 52 | "bubbles": true, 53 | "cancelable": true, 54 | "composed": true 55 | }, { 56 | "name": "match", 57 | "method": "match", 58 | "bubbles": true, 59 | "cancelable": true, 60 | "composed": true 61 | }]; } 62 | static get listeners() { return [{ 63 | "name": "document:keydown", 64 | "method": "handleKey" 65 | }]; } 66 | static get style() { return ":host {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n:host(.active) {\n display: block;\n}"; } 67 | } 68 | 69 | export { KonamiListener }; 70 | -------------------------------------------------------------------------------- /dist/konami-listener/konami-listener.sc.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | const { h } = window.KonamiListener; 3 | 4 | class KonamiListener { 5 | constructor() { 6 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 7 | this.accepted = [...new Set(this.keys)]; 8 | this.inputs = []; 9 | } 10 | handleKey(e) { 11 | let { key } = e; 12 | if (!this.accepted.includes(key)) 13 | return; 14 | (this.keys[this.inputs.length] === key) 15 | ? this.handleInput(key) 16 | : this.reset(); 17 | } 18 | handleInput(key) { 19 | this.inputs.push(key); 20 | this.input.emit({ key, index: this.inputs.length - 1 }); 21 | if (this.inputs.length === 10) { 22 | this.handleMatch(); 23 | } 24 | } 25 | handleMatch() { 26 | this.match.emit(); 27 | this.el.classList.add('active'); 28 | this.reset(); 29 | } 30 | reset() { 31 | if (this.inputs.length) { 32 | this.inputs = []; 33 | this.input.emit({ reset: true }); 34 | } 35 | } 36 | render() { 37 | return h("slot", null); 38 | } 39 | static get is() { return "konami-listener"; } 40 | static get encapsulation() { return "shadow"; } 41 | static get properties() { return { 42 | "el": { 43 | "elementRef": true 44 | }, 45 | "inputs": { 46 | "state": true 47 | } 48 | }; } 49 | static get events() { return [{ 50 | "name": "input", 51 | "method": "input", 52 | "bubbles": true, 53 | "cancelable": true, 54 | "composed": true 55 | }, { 56 | "name": "match", 57 | "method": "match", 58 | "bubbles": true, 59 | "cancelable": true, 60 | "composed": true 61 | }]; } 62 | static get listeners() { return [{ 63 | "name": "document:keydown", 64 | "method": "handleKey" 65 | }]; } 66 | static get style() { return "\n.sc-konami-listener-h {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n.active.sc-konami-listener-h {\n display: block;\n}\n"; } 67 | } 68 | 69 | export { KonamiListener }; 70 | -------------------------------------------------------------------------------- /dist/types/components.d.ts: -------------------------------------------------------------------------------- 1 | import './stencil.core'; 2 | /** 3 | * This is an autogenerated file created by the Stencil compiler. 4 | * It contains typing information for all components that exist in this project. 5 | */ 6 | /* tslint:disable */ 7 | 8 | import './stencil.core'; 9 | 10 | 11 | 12 | 13 | declare global { 14 | interface HTMLElement { 15 | componentOnReady?: () => Promise; 16 | } 17 | 18 | interface HTMLStencilElement extends HTMLElement { 19 | componentOnReady(): Promise; 20 | 21 | forceUpdate(): void; 22 | } 23 | 24 | interface HTMLAttributes {} 25 | 26 | namespace StencilComponents { 27 | 28 | interface KonamiListener { 29 | 30 | } 31 | } 32 | 33 | 34 | interface HTMLKonamiListenerElement extends StencilComponents.KonamiListener, HTMLStencilElement {} 35 | 36 | var HTMLKonamiListenerElement: { 37 | prototype: HTMLKonamiListenerElement; 38 | new (): HTMLKonamiListenerElement; 39 | }; 40 | 41 | 42 | namespace JSX { 43 | interface Element {} 44 | export interface IntrinsicElements { 45 | 'konami-listener': JSXElements.KonamiListenerAttributes; 46 | } 47 | } 48 | 49 | namespace JSXElements { 50 | 51 | export interface KonamiListenerAttributes extends HTMLAttributes { 52 | 'onInput'?: (event: CustomEvent) => void; 53 | 'onMatch'?: (event: CustomEvent) => void; 54 | } 55 | } 56 | 57 | interface HTMLElementTagNameMap { 58 | 'konami-listener': HTMLKonamiListenerElement 59 | } 60 | 61 | interface ElementTagNameMap { 62 | 'konami-listener': HTMLKonamiListenerElement; 63 | } 64 | } 65 | declare global { namespace JSX { interface StencilJSX {} } } 66 | 67 | export declare function defineCustomElements(window: any): void; -------------------------------------------------------------------------------- /dist/types/components/konami-listener/konami-listener.d.ts: -------------------------------------------------------------------------------- 1 | import '../../stencil.core'; 2 | import { EventEmitter } from '../../stencil.core'; 3 | export declare class KonamiListener { 4 | private keys; 5 | private accepted; 6 | el: HTMLElement; 7 | inputs: string[]; 8 | input: EventEmitter; 9 | match: EventEmitter; 10 | handleKey(e: KeyboardEvent): void; 11 | handleInput(key: string): void; 12 | handleMatch(): void; 13 | reset(): void; 14 | render(): JSX.Element; 15 | } 16 | -------------------------------------------------------------------------------- /dist/types/index.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natemoo-re/konami-listener/a83012a8d2544cae6e8fc1179259cb78085a0bf1/dist/types/index.d.ts -------------------------------------------------------------------------------- /dist/types/stencil.core.d.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface ComponentWillLoad { 3 | componentWillLoad: () => Promise | void; 4 | } 5 | 6 | export interface ComponentDidLoad { 7 | componentDidLoad: () => void; 8 | } 9 | 10 | export interface ComponentWillUpdate { 11 | componentWillUpdate: () => Promise | void; 12 | } 13 | 14 | export interface ComponentDidUpdate { 15 | componentDidUpdate: () => void; 16 | } 17 | 18 | export interface ComponentDidUnload { 19 | componentDidUnload: () => void; 20 | } 21 | 22 | export interface EventEmitter { 23 | emit: (data?: T) => void; 24 | } 25 | 26 | export interface EventListenerEnable { 27 | (instance: any, eventName: string, enabled: boolean, attachTo?: string|Element, passive?: boolean): void; 28 | } 29 | 30 | export interface QueueApi { 31 | tick: (cb: RafCallback) => void; 32 | read: (cb: RafCallback) => void; 33 | write: (cb: RafCallback) => void; 34 | clear?: () => void; 35 | flush?: (cb?: () => void) => void; 36 | } 37 | 38 | export interface RafCallback { 39 | (timeStamp: number): void; 40 | } 41 | 42 | declare global { 43 | namespace JSX { 44 | interface Element {} 45 | export interface IntrinsicElements {} 46 | } 47 | namespace JSXElements {} 48 | 49 | interface HTMLStencilElement extends HTMLElement { 50 | componentOnReady(): Promise; 51 | } 52 | 53 | interface HTMLAttributes {} 54 | } 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "konami-listener", 3 | "version": "0.3.0", 4 | "description": "Konami Code listener as a Vanilla Web Component", 5 | "module": "dist/esm/index.js", 6 | "main": "dist/index.js", 7 | "types": "dist/types/components.d.ts", 8 | "collection": "dist/collection/collection-manifest.json", 9 | "files": [ 10 | "dist/" 11 | ], 12 | "scripts": { 13 | "build": "stencil build", 14 | "start": "stencil build --dev --watch --serve", 15 | "test": "jest", 16 | "test.watch": "jest --watch" 17 | }, 18 | "devDependencies": { 19 | "@stencil/core": "^0.11.0", 20 | "@types/jest": "^23.3.1", 21 | "jest": "^23.4.2" 22 | }, 23 | "repository": { 24 | "type": "git", 25 | "url": "git+https://github.com/natemoo-re/konami-listener.git" 26 | }, 27 | "author": "Nate Moore", 28 | "license": "MIT", 29 | "bugs": { 30 | "url": "https://github.com/natemoo-re/konami-listener" 31 | }, 32 | "homepage": "http://projects.natemoo.re/konami-listener", 33 | "jest": { 34 | "transform": { 35 | "^.+\\.(ts|tsx)$": "/node_modules/@stencil/core/testing/jest.preprocessor.js" 36 | }, 37 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$", 38 | "moduleFileExtensions": [ 39 | "ts", 40 | "tsx", 41 | "js", 42 | "json", 43 | "jsx" 44 | ] 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ![Built With Stencil](https://img.shields.io/badge/-Built%20With%20Stencil-16161d.svg?logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMSwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1MTIgNTEyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI%2BCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI%2BCgkuc3Qwe2ZpbGw6I0ZGRkZGRjt9Cjwvc3R5bGU%2BCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik00MjQuNywzNzMuOWMwLDM3LjYtNTUuMSw2OC42LTkyLjcsNjguNkgxODAuNGMtMzcuOSwwLTkyLjctMzAuNy05Mi43LTY4LjZ2LTMuNmgzMzYuOVYzNzMuOXoiLz4KPHBhdGggY2xhc3M9InN0MCIgZD0iTTQyNC43LDI5Mi4xSDE4MC40Yy0zNy42LDAtOTIuNy0zMS05Mi43LTY4LjZ2LTMuNkgzMzJjMzcuNiwwLDkyLjcsMzEsOTIuNyw2OC42VjI5Mi4xeiIvPgo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNNDI0LjcsMTQxLjdIODcuN3YtMy42YzAtMzcuNiw1NC44LTY4LjYsOTIuNy02OC42SDMzMmMzNy45LDAsOTIuNywzMC43LDkyLjcsNjguNlYxNDEuN3oiLz4KPC9zdmc%2BCg%3D%3D&colorA=16161d&style=flat-square) 2 | ![NPM Version](https://img.shields.io/npm/v/konami-listener.svg?style=flat-square) 3 | 4 | # Konami Listener 5 | 6 | `` is a web component that listens for the Konami Code. When a match is triggered, it will automatically show any content placed inside of the component, like so `

Hello world!

` 7 | 8 | See the demo at [http://projects.natemoo.re/konami-listener](http://projects.natemoo.re/konami-listener) 9 | 10 | ## Getting Started 11 | 12 | ### Script tag 13 | 14 | - Add the javascript module in the head of your index.html file 15 | ```html 16 | 17 | ``` 18 | - Use `` anywhere in your templates, JSX, html, etc 19 | 20 | ### Node Modules 21 | - Run `npm install konami-listener --save` 22 | - Add the javascript module in the head of your index.html file 23 | ```html 24 | 25 | ``` 26 | - Use `` anywhere in your templates, JSX, html, etc 27 | 28 | ### In a stencil-starter app 29 | - Run `npm install konami-listener --save` 30 | - Add `{ name: 'konami-listener' }` to your [collections](https://github.com/ionic-team/stencil-starter/blob/master/stencil.config.js#L5) 31 | - Use `` anywhere in your templates, JSX, html, etc 32 | 33 | ## API 34 | ### Simple Usage 35 | `` allows you to place any content inside of it. It will then be shown when the code is matched. By default, the container is absolutely positioned over the entire page, so style the internal markup accordingly. 36 | ```html 37 | 38 |

💩

39 |
40 | ``` 41 | 42 | ### Events 43 | `` emits the following custom events 44 | | Event Name | Description | Data | 45 | | ----------- | ------------------------------------------------ | -------------------------------- | 46 | | input | Emitted on each keydown as the code progresses | `{ key: string, index: number }` | 47 | | match | Emitted on the final match | none | 48 | 49 | To listen for events, just use `addEventListener`. Since it uses custom HTML events, the data payload is bound to `event.detail`. 50 | ```js 51 | const konami = document.querySelector('konami-listener'); 52 | konami.addEventListener('match', () => { 53 | console.log('Konami Code matched!'); 54 | }) 55 | konami.addEventListener('input', (e) => { 56 | const { key, index } = e.detail; 57 | console.log('Konami Code progress', { key, index }); 58 | }) 59 | ``` 60 | 61 | ## Framework Integration 62 | If you'd like to drop this component into Angular, React, or any other framework, take a look at Stencil's [Framework Integration guide](https://stenciljs.com/docs/framework-integration) -------------------------------------------------------------------------------- /src/components.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This is an autogenerated file created by the Stencil compiler. 3 | * It contains typing information for all components that exist in this project. 4 | */ 5 | /* tslint:disable */ 6 | 7 | import '@stencil/core'; 8 | 9 | 10 | 11 | 12 | declare global { 13 | interface HTMLElement { 14 | componentOnReady?: () => Promise; 15 | } 16 | 17 | interface HTMLStencilElement extends HTMLElement { 18 | componentOnReady(): Promise; 19 | 20 | forceUpdate(): void; 21 | } 22 | 23 | interface HTMLAttributes {} 24 | 25 | namespace StencilComponents { 26 | 27 | interface KonamiListener { 28 | 29 | } 30 | } 31 | 32 | 33 | interface HTMLKonamiListenerElement extends StencilComponents.KonamiListener, HTMLStencilElement {} 34 | 35 | var HTMLKonamiListenerElement: { 36 | prototype: HTMLKonamiListenerElement; 37 | new (): HTMLKonamiListenerElement; 38 | }; 39 | 40 | 41 | namespace JSX { 42 | interface Element {} 43 | export interface IntrinsicElements { 44 | 'konami-listener': JSXElements.KonamiListenerAttributes; 45 | } 46 | } 47 | 48 | namespace JSXElements { 49 | 50 | export interface KonamiListenerAttributes extends HTMLAttributes { 51 | 'onInput'?: (event: CustomEvent) => void; 52 | 'onMatch'?: (event: CustomEvent) => void; 53 | } 54 | } 55 | 56 | interface HTMLElementTagNameMap { 57 | 'konami-listener': HTMLKonamiListenerElement 58 | } 59 | 60 | interface ElementTagNameMap { 61 | 'konami-listener': HTMLKonamiListenerElement; 62 | } 63 | } 64 | declare global { namespace JSX { interface StencilJSX {} } } 65 | 66 | export declare function defineCustomElements(window: any): void; -------------------------------------------------------------------------------- /src/components/konami-listener/konami-listener.css: -------------------------------------------------------------------------------- 1 | :host { 2 | display: none; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | bottom: 0; 7 | right: 0; 8 | } 9 | 10 | :host(.active) { 11 | display: block; 12 | } 13 | -------------------------------------------------------------------------------- /src/components/konami-listener/konami-listener.spec.ts: -------------------------------------------------------------------------------- 1 | import { flush, render } from '@stencil/core/testing'; 2 | import { KonamiListener } from './konami-listener'; 3 | 4 | describe('konami-listener', () => { 5 | it('should build', () => { 6 | expect(new KonamiListener()).toBeTruthy(); 7 | }); 8 | 9 | describe('keyboard', () => { 10 | let element: HTMLElement; 11 | 12 | let press = function(key) { 13 | let event = new KeyboardEvent('keydown', { key }); 14 | document.dispatchEvent(event); 15 | } 16 | 17 | beforeEach(async () => { 18 | element = await render({ 19 | components: [KonamiListener], 20 | html: '' 21 | }); 22 | }); 23 | 24 | it('should recognize ArrowUp', () => { 25 | // press('ArrowUp'); 26 | expect(element).toBeTruthy(); 27 | }); 28 | }) 29 | 30 | // describe('rendering', () => { 31 | // let element; 32 | // beforeEach(async () => { 33 | // element = await render({ 34 | // components: [KonamiListener], 35 | // html: '' 36 | // }); 37 | // }); 38 | 39 | // it('should work without parameters', () => { 40 | // expect(element..trim()).toEqual('Hello, World! I\'m'); 41 | // }); 42 | 43 | // it('should work with a first name', async () => { 44 | // element.first = 'Peter'; 45 | // await flush(element); 46 | // expect(element.textContent.trim()).toEqual('Hello, World! I\'m Peter'); 47 | // }); 48 | 49 | // it('should work with a last name', async () => { 50 | // element.last = 'Parker'; 51 | // await flush(element); 52 | // expect(element.textContent.trim()).toEqual('Hello, World! I\'m Parker'); 53 | // }); 54 | 55 | // it('should work with both a first and a last name', async () => { 56 | // element.first = 'Peter' 57 | // element.last = 'Parker'; 58 | // await flush(element); 59 | // expect(element.textContent.trim()).toEqual('Hello, World! I\'m Peter Parker'); 60 | // }); 61 | // }); 62 | }); -------------------------------------------------------------------------------- /src/components/konami-listener/konami-listener.tsx: -------------------------------------------------------------------------------- 1 | import { Component, State, Listen, Event, EventEmitter, Element } from '@stencil/core'; 2 | 3 | @Component({ 4 | tag: 'konami-listener', 5 | styleUrl: 'konami-listener.css', 6 | shadow: true 7 | }) 8 | export class KonamiListener { 9 | 10 | private keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 11 | private accepted = [...new Set(this.keys)]; 12 | 13 | @Element() el: HTMLElement; 14 | 15 | @State() inputs: string[] = []; 16 | 17 | @Event() input: EventEmitter; 18 | @Event() match: EventEmitter; 19 | 20 | @Listen('document:keydown') 21 | handleKey(e: KeyboardEvent) { 22 | let { key } = e; 23 | if (!this.accepted.includes(key)) return; 24 | 25 | (this.keys[this.inputs.length] === key) 26 | ? this.handleInput(key) 27 | : this.reset(); 28 | } 29 | 30 | handleInput(key: string) { 31 | this.inputs.push(key); 32 | this.input.emit({ key, index: this.inputs.length - 1 }); 33 | 34 | if (this.inputs.length === 10) { 35 | this.handleMatch(); 36 | } 37 | } 38 | 39 | handleMatch() { 40 | this.match.emit(); 41 | this.el.classList.add('active'); 42 | this.reset(); 43 | } 44 | 45 | reset() { 46 | if (this.inputs.length) { 47 | this.inputs = []; 48 | this.input.emit({ reset: true }); 49 | } 50 | } 51 | 52 | render() { 53 | return 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/natemoo-re/konami-listener/a83012a8d2544cae6e8fc1179259cb78085a0bf1/src/index.d.ts -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Stencil Component Starter 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

Konami Listener

19 |

A web component to detect the Konami Code on any website! It's as simple as including the script and a <konami-listener> tag.

20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 |
31 |
32 | 33 | 34 | 35 | 36 |
37 |
38 | 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | 48 |
49 |
50 | 51 | 52 | 53 | 54 |
55 |
56 | 57 | 58 | 59 | 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 |

made with <3 by

@n_moore
91 |

see the code on

Github
92 |
93 |
94 | 95 | 96 |
97 | 98 |

Woohoo!

99 |
100 |
101 | 102 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | -------------------------------------------------------------------------------- /src/main.css: -------------------------------------------------------------------------------- 1 | @keyframes fade { 2 | 0% { 3 | opacity: 0.5; 4 | } 5 | 100% { 6 | opacity: 0; 7 | } 8 | } 9 | 10 | :root { 11 | --color-red: #f4511e; 12 | --header-size: 200px; 13 | --footer-size: 56px; 14 | } 15 | 16 | html, body { 17 | padding: 0; 18 | margin: 0; 19 | } 20 | 21 | header { 22 | box-sizing: border-box; 23 | width: 100%; 24 | height: var(--header-size); 25 | display: flex; 26 | flex-flow: column nowrap; 27 | align-items: center; 28 | justify-content: center; 29 | text-align: center; 30 | padding: 0 12px; 31 | max-width: 570px; 32 | margin: 0 auto; 33 | } 34 | header h1 { 35 | margin: 0; 36 | font-family: 'Press Start 2P', sans-serif; 37 | line-height: 1.2; 38 | } 39 | header p { 40 | font-family: sans-serif; 41 | color: rgba(0,0,0,0.54); 42 | } 43 | header code { 44 | box-sizing: border-box; 45 | padding: 1px 4px 0 4px; 46 | line-height: 1; 47 | background-color: rgba(0, 0, 0, 0.12); 48 | border-radius: 4px; 49 | white-space: nowrap; 50 | } 51 | 52 | 53 | footer { 54 | box-sizing: border-box; 55 | height: var(--footer-size); 56 | background-color: red; 57 | width: 100%; 58 | display: flex; 59 | align-items: center; 60 | } 61 | footer .inner { 62 | box-sizing: border-box; 63 | width: 100%; 64 | display: flex; 65 | align-items: center; 66 | justify-content: space-between; 67 | max-width: 570px; 68 | margin: 0 auto; 69 | } 70 | footer .inner div { 71 | box-sizing: border-box; 72 | text-align: center; 73 | padding: 0 12px; 74 | } 75 | footer h4 { 76 | margin: 0; 77 | font-family: sans-serif; 78 | font-weight: 400; 79 | color: #FFF; 80 | } 81 | footer a { 82 | font-family: 'Press Start 2P', sans-serif; 83 | font-size: 14px; 84 | color: #FFF; 85 | } 86 | 87 | main, .prize { 88 | height: calc(calc(100vh - var(--header-size)) - var(--footer-size)); 89 | width: 100%; 90 | max-width: calc(1000px / 4); 91 | 92 | display: flex; 93 | align-items: center; 94 | justify-content: center; 95 | margin: 0 auto; 96 | } 97 | 98 | main.hidden { 99 | opacity: 0; 100 | } 101 | 102 | .prize { 103 | flex-flow: column nowrap; 104 | align-items: center; 105 | justify-content: center; 106 | height: 100vh; 107 | font-size: 126px; 108 | } 109 | .prize h2 { 110 | font-size: 32px; 111 | font-family: 'Press Start 2P', sans-serif; 112 | color: var(--color-red); 113 | margin: 0; 114 | line-height: 1; 115 | 116 | } 117 | 118 | .inputs { 119 | width: 100%; 120 | flex-flow: row wrap; 121 | display: flex; 122 | align-items: center; 123 | justify-content: space-between; 124 | transform: scale(.7); 125 | } 126 | 127 | .inputs > div { 128 | --size: 100px; 129 | margin: 12px; 130 | width: var(--size); 131 | height: var(--size); 132 | } 133 | 134 | .inputs > div > svg > path.fixed { 135 | fill: rgba(0,0,0,0.32); 136 | /* stroke-width: 4px; */ 137 | z-index: 1; 138 | } 139 | .inputs > div > svg > path:not(.fixed) { 140 | transition: 250ms ease-out all; 141 | fill: none; 142 | stroke-width: 2px; 143 | opacity: 0.5; 144 | z-index: -1; 145 | stroke: var(--color-red); 146 | 147 | opacity: 0; 148 | transform-origin: center center; 149 | } 150 | 151 | .inputs > div > svg { 152 | overflow: visible; 153 | } 154 | 155 | .inputs > div.active > svg > path.fixed { 156 | fill: var(--color-red); 157 | stroke: transparent; 158 | } 159 | .inputs > div.active > svg > path:not(.fixed) { 160 | display: block; 161 | opacity: 1; 162 | transform: scale(1.5); 163 | animation: 150ms 100ms fade forwards; 164 | } 165 | 166 | div.up.active > svg > path:not(.fixed) { 167 | transform: translateY(-70px); 168 | } 169 | div.down.active > svg > path:not(.fixed) { 170 | transform: translateY(70px); 171 | } 172 | div.left.active > svg > path:not(.fixed) { 173 | transform: translateX(-70px); 174 | } 175 | div.right.active > svg > path:not(.fixed) { 176 | transform: translateX(70px); 177 | } 178 | div.B.active > svg > path:not(.fixed) { 179 | transform: scale(1.75); 180 | } 181 | div.A.active > svg > path:not(.fixed) { 182 | transform: scale(1.75); 183 | } 184 | @media only screen and (min-width: 555px) { 185 | header { 186 | margin-bottom: calc(var(--header-size) / -4); 187 | } 188 | main { 189 | padding-bottom: calc(var(--header-size) / 4); 190 | } 191 | } 192 | 193 | @media only screen and (min-width: 1180px) { 194 | main { 195 | max-width: 1180px; 196 | } 197 | footer { 198 | justify-content: center; 199 | } 200 | .inputs { 201 | transform: scale(1); 202 | } 203 | 204 | .inputs > div { 205 | margin: 0; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /stencil.config.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '@stencil/core'; 2 | 3 | export const config: Config = { 4 | namespace: 'konami-listener', 5 | outputTargets: [ 6 | { type: 'dist' }, 7 | { 8 | type: 'www', 9 | serviceWorker: null 10 | } 11 | ], 12 | copy: [ 13 | { src: 'main.css' } 14 | ] 15 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": true, 4 | "allowUnreachableCode": false, 5 | "declaration": false, 6 | "experimentalDecorators": true, 7 | "lib": [ 8 | "dom", 9 | "es2017" 10 | ], 11 | "moduleResolution": "node", 12 | "module": "es2015", 13 | "target": "es2017", 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "jsx": "react", 17 | "jsxFactory": "h" 18 | }, 19 | "include": [ 20 | "src", 21 | "types/jsx.d.ts" 22 | ], 23 | "exclude": [ 24 | "node_modules" 25 | ] 26 | } -------------------------------------------------------------------------------- /www/build/konami-listener.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Built with http://stenciljs.com 3 | * 2018-08-19T13:38:16 4 | */ 5 | (function(win, doc, namespace, fsNamespace, resourcesUrl, appCore, appCoreSsr, appCorePolyfilled, hydratedCssClass, components) { 6 | 7 | function init(win, doc, namespace, fsNamespace, resourcesUrl, appCore, appCorePolyfilled, hydratedCssClass, components, HTMLElementPrototype, App, x, y, scriptElm) { 8 | // create global namespace if it doesn't already exist 9 | App = win[namespace] = win[namespace] || {}; 10 | App.components = components; 11 | y = components.filter(function (c) { return c[2]; }).map(function (c) { return c[0]; }); 12 | if (y.length) { 13 | // auto hide components until they been fully hydrated 14 | // reusing the "x" and "i" variables from the args for funzies 15 | x = doc.createElement('style'); 16 | x.innerHTML = y.join() + '{visibility:hidden}.' + hydratedCssClass + '{visibility:inherit}'; 17 | x.setAttribute('data-styles', ''); 18 | doc.head.insertBefore(x, doc.head.firstChild); 19 | } 20 | createComponentOnReadyPrototype(win, namespace, HTMLElementPrototype); 21 | resourcesUrl = resourcesUrl || App.resourcesUrl; 22 | // figure out the script element for this current script 23 | y = doc.querySelectorAll('script'); 24 | for (x = y.length - 1; x >= 0; x--) { 25 | scriptElm = y[x]; 26 | if (scriptElm.src || scriptElm.hasAttribute('data-resources-url')) { 27 | break; 28 | } 29 | } 30 | // get the resource path attribute on this script element 31 | y = scriptElm.getAttribute('data-resources-url'); 32 | if (!resourcesUrl && y) { 33 | // the script element has a data-resources-url attribute, always use that 34 | resourcesUrl = y; 35 | } 36 | if (!resourcesUrl && scriptElm.src) { 37 | // we don't have an exact resourcesUrl, so let's 38 | // figure it out relative to this script's src and app's filesystem namespace 39 | y = scriptElm.src.split('/').slice(0, -1); 40 | resourcesUrl = (y.join('/')) + (y.length ? '/' : '') + fsNamespace + '/'; 41 | } 42 | // request the core this browser needs 43 | // test for native support of custom elements and fetch 44 | // if either of those are not supported, then use the core w/ polyfills 45 | // also check if the page was build with ssr or not 46 | x = doc.createElement('script'); 47 | if (usePolyfills(win, win.location, x, 'import("")')) { 48 | // requires the es5/polyfilled core 49 | x.src = resourcesUrl + appCorePolyfilled; 50 | } 51 | else { 52 | // let's do this! 53 | x.src = resourcesUrl + appCore; 54 | x.setAttribute('type', 'module'); 55 | x.setAttribute('crossorigin', true); 56 | } 57 | x.setAttribute('data-resources-url', resourcesUrl); 58 | x.setAttribute('data-namespace', fsNamespace); 59 | doc.head.appendChild(x); 60 | } 61 | function usePolyfills(win, location, scriptElm, dynamicImportTest) { 62 | // fyi, dev mode has verbose if/return statements 63 | // but it minifies to a nice 'lil one-liner ;) 64 | if (location.search.indexOf('core=esm') > 0) { 65 | // force esm build 66 | return false; 67 | } 68 | if ((location.search.indexOf('core=es5') > 0) || 69 | (location.protocol === 'file:') || 70 | (!(win.customElements && win.customElements.define)) || 71 | (!win.fetch) || 72 | (!(win.CSS && win.CSS.supports && win.CSS.supports('color', 'var(--c)'))) || 73 | (!('noModule' in scriptElm))) { 74 | // es5 build w/ polyfills 75 | return true; 76 | } 77 | // final test to see if this browser support dynamic imports 78 | return doesNotSupportsDynamicImports(dynamicImportTest); 79 | } 80 | function doesNotSupportsDynamicImports(dynamicImportTest) { 81 | try { 82 | new Function(dynamicImportTest); 83 | return false; 84 | } 85 | catch (e) { } 86 | return true; 87 | } 88 | function createComponentOnReadyPrototype(win, namespace, HTMLElementPrototype) { 89 | (win['s-apps'] = win['s-apps'] || []).push(namespace); 90 | if (!HTMLElementPrototype.componentOnReady) { 91 | HTMLElementPrototype.componentOnReady = function componentOnReady() { 92 | /*tslint:disable*/ 93 | var elm = this; 94 | function executor(resolve) { 95 | if (elm.nodeName.indexOf('-') > 0) { 96 | // window hasn't loaded yet and there's a 97 | // good chance this is a custom element 98 | var apps = win['s-apps']; 99 | var appsReady = 0; 100 | // loop through all the app namespaces 101 | for (var i = 0; i < apps.length; i++) { 102 | // see if this app has "componentOnReady" setup 103 | if (win[apps[i]].componentOnReady) { 104 | // this app's core has loaded call its "componentOnReady" 105 | if (win[apps[i]].componentOnReady(elm, resolve)) { 106 | // this component does belong to this app and would 107 | // have fired off the resolve fn 108 | // let's stop here, we're good 109 | return; 110 | } 111 | appsReady++; 112 | } 113 | } 114 | if (appsReady < apps.length) { 115 | // not all apps are ready yet 116 | // add it to the queue to be figured out when they are 117 | (win['s-cr'] = win['s-cr'] || []).push([elm, resolve]); 118 | return; 119 | } 120 | } 121 | // not a recognized app component 122 | resolve(null); 123 | } 124 | // callback wasn't provided, let's return a promise 125 | if (win.Promise) { 126 | // use native/polyfilled promise 127 | return new win.Promise(executor); 128 | } 129 | // promise may not have been polyfilled yet 130 | return { then: executor }; 131 | }; 132 | } 133 | } 134 | 135 | 136 | init(win, doc, namespace, fsNamespace, resourcesUrl, appCore, appCoreSsr, appCorePolyfilled, hydratedCssClass, components); 137 | 138 | })(window, document, "KonamiListener","konami-listener",0,"konami-listener.core.js","es5-build-disabled.js","hydrated",[["konami-listener","konami-listener",1,[["el",7],["inputs",5]],1,[["document:keydown","handleKey"]]]],HTMLElement.prototype); -------------------------------------------------------------------------------- /www/build/konami-listener/es5-build-disabled.js: -------------------------------------------------------------------------------- 1 | setTimeout(function(){ 2 | document.body.innerHTML = '

This Stencil app is disabled for this browser.

Developers:

  • ES5 builds are disabled during development to take advantage of 2x faster build times.
  • Please see the example below or our config docs if you would like to develop on a browser that does not fully support ES2017 and custom elements.
  • Note that by default, ES5 builds and polyfills are enabled during production builds.
  • When testing browsers it is recommended to always test in production mode, and ES5 builds should always be enabled during production builds.
  • This is only an experiement and if it slows down app development then we will revert this and enable ES5 builds during dev.

Enabling ES5 builds during development:

    npm run dev --es5  

For stencil-component-starter, use:

    npm start --es5  

Enabling full production builds during development:

    npm run dev --prod  

For stencil-component-starter, use:

    npm start --prod  

Current Browser\'s Support:

Current Browser:

      
'; 3 | 4 | document.getElementById('current-browser-output').textContent = window.navigator.userAgent; 5 | document.getElementById('es-modules-test').textContent = !!('noModule' in document.createElement('script')); 6 | document.getElementById('custom-elements-test').textContent = !!(window.customElements); 7 | document.getElementById('css-variables-test').textContent = !!(window.CSS && window.CSS.supports && window.CSS.supports('color', 'var(--c)')); 8 | document.getElementById('fetch-test').textContent = !!(window.fetch); 9 | }, 10) -------------------------------------------------------------------------------- /www/build/konami-listener/konami-listener.global.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | (function(namespace,resourcesUrl){"use strict"; 3 | 4 | })("KonamiListener"); -------------------------------------------------------------------------------- /www/build/konami-listener/konami-listener.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | const { h } = window.KonamiListener; 3 | 4 | class KonamiListener { 5 | constructor() { 6 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 7 | this.accepted = [...new Set(this.keys)]; 8 | this.inputs = []; 9 | } 10 | handleKey(e) { 11 | let { key } = e; 12 | if (!this.accepted.includes(key)) 13 | return; 14 | (this.keys[this.inputs.length] === key) 15 | ? this.handleInput(key) 16 | : this.reset(); 17 | } 18 | handleInput(key) { 19 | this.inputs.push(key); 20 | this.input.emit({ key, index: this.inputs.length - 1 }); 21 | if (this.inputs.length === 10) { 22 | this.handleMatch(); 23 | } 24 | } 25 | handleMatch() { 26 | this.match.emit(); 27 | this.el.classList.add('active'); 28 | this.reset(); 29 | } 30 | reset() { 31 | if (this.inputs.length) { 32 | this.inputs = []; 33 | this.input.emit({ reset: true }); 34 | } 35 | } 36 | render() { 37 | return h("slot", null); 38 | } 39 | static get is() { return "konami-listener"; } 40 | static get encapsulation() { return "shadow"; } 41 | static get properties() { return { 42 | "el": { 43 | "elementRef": true 44 | }, 45 | "inputs": { 46 | "state": true 47 | } 48 | }; } 49 | static get events() { return [{ 50 | "name": "input", 51 | "method": "input", 52 | "bubbles": true, 53 | "cancelable": true, 54 | "composed": true 55 | }, { 56 | "name": "match", 57 | "method": "match", 58 | "bubbles": true, 59 | "cancelable": true, 60 | "composed": true 61 | }]; } 62 | static get listeners() { return [{ 63 | "name": "document:keydown", 64 | "method": "handleKey" 65 | }]; } 66 | static get style() { return ":host {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n:host(.active) {\n display: block;\n}"; } 67 | } 68 | 69 | export { KonamiListener }; 70 | -------------------------------------------------------------------------------- /www/build/konami-listener/konami-listener.registry.json: -------------------------------------------------------------------------------- 1 | { 2 | "namespace": "KonamiListener", 3 | "fsNamespace": "konami-listener", 4 | "loader": "../konami-listener.js", 5 | "corePolyfilled": "es5-build-disabled.js", 6 | "global": "konami-listener.global.js", 7 | "core": "konami-listener.core.js", 8 | "components": { 9 | "konami-listener": { 10 | "bundleIds": { 11 | "$": "konami-listener" 12 | }, 13 | "encapsulation": "shadow" 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /www/build/konami-listener/konami-listener.sc.js: -------------------------------------------------------------------------------- 1 | /*! Built with http://stenciljs.com */ 2 | const { h } = window.KonamiListener; 3 | 4 | class KonamiListener { 5 | constructor() { 6 | this.keys = ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "b", "a"]; 7 | this.accepted = [...new Set(this.keys)]; 8 | this.inputs = []; 9 | } 10 | handleKey(e) { 11 | let { key } = e; 12 | if (!this.accepted.includes(key)) 13 | return; 14 | (this.keys[this.inputs.length] === key) 15 | ? this.handleInput(key) 16 | : this.reset(); 17 | } 18 | handleInput(key) { 19 | this.inputs.push(key); 20 | this.input.emit({ key, index: this.inputs.length - 1 }); 21 | if (this.inputs.length === 10) { 22 | this.handleMatch(); 23 | } 24 | } 25 | handleMatch() { 26 | this.match.emit(); 27 | this.el.classList.add('active'); 28 | this.reset(); 29 | } 30 | reset() { 31 | if (this.inputs.length) { 32 | this.inputs = []; 33 | this.input.emit({ reset: true }); 34 | } 35 | } 36 | render() { 37 | return h("slot", null); 38 | } 39 | static get is() { return "konami-listener"; } 40 | static get encapsulation() { return "shadow"; } 41 | static get properties() { return { 42 | "el": { 43 | "elementRef": true 44 | }, 45 | "inputs": { 46 | "state": true 47 | } 48 | }; } 49 | static get events() { return [{ 50 | "name": "input", 51 | "method": "input", 52 | "bubbles": true, 53 | "cancelable": true, 54 | "composed": true 55 | }, { 56 | "name": "match", 57 | "method": "match", 58 | "bubbles": true, 59 | "cancelable": true, 60 | "composed": true 61 | }]; } 62 | static get listeners() { return [{ 63 | "name": "document:keydown", 64 | "method": "handleKey" 65 | }]; } 66 | static get style() { return "\n.sc-konami-listener-h {\n display: none;\n position: absolute;\n top: 0;\n left: 0;\n bottom: 0;\n right: 0;\n}\n\n.active.sc-konami-listener-h {\n display: block;\n}\n"; } 67 | } 68 | 69 | export { KonamiListener }; 70 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stencil Component Starter 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |

Konami Listener

17 |

A web component to detect the Konami Code on any website! It's as simple as including the script and a <konami-listener> tag.

18 | 19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 | 33 | 34 |
35 |
36 | 37 | 38 | 39 | 40 |
41 |
42 | 43 | 44 | 45 | 46 |
47 |
48 | 49 | 50 | 51 | 52 |
53 |
54 | 55 | 56 | 57 | 58 |
59 |
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 |

made with <3 by

@n_moore
89 |

see the code on

Github
90 |
91 |
92 | 93 | 94 |
95 | 96 |

Woohoo!

97 |
98 |
99 | 100 | 117 | 118 | 119 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /www/main.css: -------------------------------------------------------------------------------- 1 | @keyframes fade { 2 | 0% { 3 | opacity: 0.5; 4 | } 5 | 100% { 6 | opacity: 0; 7 | } 8 | } 9 | 10 | :root { 11 | --color-red: #f4511e; 12 | --header-size: 200px; 13 | --footer-size: 56px; 14 | } 15 | 16 | html, body { 17 | padding: 0; 18 | margin: 0; 19 | } 20 | 21 | header { 22 | box-sizing: border-box; 23 | width: 100%; 24 | height: var(--header-size); 25 | display: flex; 26 | flex-flow: column nowrap; 27 | align-items: center; 28 | justify-content: center; 29 | text-align: center; 30 | padding: 0 12px; 31 | max-width: 570px; 32 | margin: 0 auto; 33 | } 34 | header h1 { 35 | margin: 0; 36 | font-family: 'Press Start 2P', sans-serif; 37 | line-height: 1.2; 38 | } 39 | header p { 40 | font-family: sans-serif; 41 | color: rgba(0,0,0,0.54); 42 | } 43 | header code { 44 | box-sizing: border-box; 45 | padding: 1px 4px 0 4px; 46 | line-height: 1; 47 | background-color: rgba(0, 0, 0, 0.12); 48 | border-radius: 4px; 49 | white-space: nowrap; 50 | } 51 | 52 | 53 | footer { 54 | box-sizing: border-box; 55 | height: var(--footer-size); 56 | background-color: red; 57 | width: 100%; 58 | display: flex; 59 | align-items: center; 60 | } 61 | footer .inner { 62 | box-sizing: border-box; 63 | width: 100%; 64 | display: flex; 65 | align-items: center; 66 | justify-content: space-between; 67 | max-width: 570px; 68 | margin: 0 auto; 69 | } 70 | footer .inner div { 71 | box-sizing: border-box; 72 | text-align: center; 73 | padding: 0 12px; 74 | } 75 | footer h4 { 76 | margin: 0; 77 | font-family: sans-serif; 78 | font-weight: 400; 79 | color: #FFF; 80 | } 81 | footer a { 82 | font-family: 'Press Start 2P', sans-serif; 83 | font-size: 14px; 84 | color: #FFF; 85 | } 86 | 87 | main, .prize { 88 | height: calc(calc(100vh - var(--header-size)) - var(--footer-size)); 89 | width: 100%; 90 | max-width: calc(1000px / 4); 91 | 92 | display: flex; 93 | align-items: center; 94 | justify-content: center; 95 | margin: 0 auto; 96 | } 97 | 98 | main.hidden { 99 | opacity: 0; 100 | } 101 | 102 | .prize { 103 | flex-flow: column nowrap; 104 | align-items: center; 105 | justify-content: center; 106 | height: 100vh; 107 | font-size: 126px; 108 | } 109 | .prize h2 { 110 | font-size: 32px; 111 | font-family: 'Press Start 2P', sans-serif; 112 | color: var(--color-red); 113 | margin: 0; 114 | line-height: 1; 115 | 116 | } 117 | 118 | .inputs { 119 | width: 100%; 120 | flex-flow: row wrap; 121 | display: flex; 122 | align-items: center; 123 | justify-content: space-between; 124 | transform: scale(.7); 125 | } 126 | 127 | .inputs > div { 128 | --size: 100px; 129 | margin: 12px; 130 | width: var(--size); 131 | height: var(--size); 132 | } 133 | 134 | .inputs > div > svg > path.fixed { 135 | fill: rgba(0,0,0,0.32); 136 | /* stroke-width: 4px; */ 137 | z-index: 1; 138 | } 139 | .inputs > div > svg > path:not(.fixed) { 140 | transition: 250ms ease-out all; 141 | fill: none; 142 | stroke-width: 2px; 143 | opacity: 0.5; 144 | z-index: -1; 145 | stroke: var(--color-red); 146 | 147 | opacity: 0; 148 | transform-origin: center center; 149 | } 150 | 151 | .inputs > div > svg { 152 | overflow: visible; 153 | } 154 | 155 | .inputs > div.active > svg > path.fixed { 156 | fill: var(--color-red); 157 | stroke: transparent; 158 | } 159 | .inputs > div.active > svg > path:not(.fixed) { 160 | display: block; 161 | opacity: 1; 162 | transform: scale(1.5); 163 | animation: 150ms 100ms fade forwards; 164 | } 165 | 166 | div.up.active > svg > path:not(.fixed) { 167 | transform: translateY(-70px); 168 | } 169 | div.down.active > svg > path:not(.fixed) { 170 | transform: translateY(70px); 171 | } 172 | div.left.active > svg > path:not(.fixed) { 173 | transform: translateX(-70px); 174 | } 175 | div.right.active > svg > path:not(.fixed) { 176 | transform: translateX(70px); 177 | } 178 | div.B.active > svg > path:not(.fixed) { 179 | transform: scale(1.75); 180 | } 181 | div.A.active > svg > path:not(.fixed) { 182 | transform: scale(1.75); 183 | } 184 | @media only screen and (min-width: 555px) { 185 | header { 186 | margin-bottom: calc(var(--header-size) / -4); 187 | } 188 | main { 189 | padding-bottom: calc(var(--header-size) / 4); 190 | } 191 | } 192 | 193 | @media only screen and (min-width: 1180px) { 194 | main { 195 | max-width: 1180px; 196 | } 197 | footer { 198 | justify-content: center; 199 | } 200 | .inputs { 201 | transform: scale(1); 202 | } 203 | 204 | .inputs > div { 205 | margin: 0; 206 | } 207 | } 208 | --------------------------------------------------------------------------------