Repository: codelab-fun/codelab
Branch: master
Commit: fcd3caf8c656
Files: 1647
Total size: 6.9 MB
Directory structure:
gitextract_yuiyff9g/
├── .editorconfig
├── .firebaserc
├── .flooignore
├── .gitattributes
├── .gitignore
├── .nvmrc
├── .prettierignore
├── .prettierrc
├── .travis.yml
├── .vscode/
│ └── extensions.json
├── LICENSE
├── README.md
├── angular.json
├── apps/
│ ├── angular-thirty-seconds/
│ │ ├── browserslist
│ │ ├── karma.conf.js
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── create-snippet/
│ │ │ │ │ ├── create-snippet.component.html
│ │ │ │ │ ├── create-snippet.component.scss
│ │ │ │ │ ├── create-snippet.component.spec.ts
│ │ │ │ │ ├── create-snippet.component.ts
│ │ │ │ │ ├── create-snippet.module.ts
│ │ │ │ │ ├── snippet-info/
│ │ │ │ │ │ ├── snippet-info.component.html
│ │ │ │ │ │ ├── snippet-info.component.scss
│ │ │ │ │ │ └── snippet-info.component.ts
│ │ │ │ │ ├── snippet-modal/
│ │ │ │ │ │ ├── snippet-overview.component.html
│ │ │ │ │ │ ├── snippet-overview.component.scss
│ │ │ │ │ │ └── snippet-overview.component.ts
│ │ │ │ │ └── snippet-spinner/
│ │ │ │ │ ├── snippet-spinner.component.html
│ │ │ │ │ ├── snippet-spinner.component.scss
│ │ │ │ │ └── snippet-spinner.component.ts
│ │ │ │ ├── pull-requests-list/
│ │ │ │ │ ├── pull-requests-list.component.html
│ │ │ │ │ ├── pull-requests-list.component.scss
│ │ │ │ │ └── pull-requests-list.component.ts
│ │ │ │ └── shared/
│ │ │ │ ├── angular-sample.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── consts.ts
│ │ │ │ ├── functions/
│ │ │ │ │ ├── generate-snippet.spec.ts
│ │ │ │ │ ├── generate-snippet.ts
│ │ │ │ │ ├── parse-snippet.spec.ts
│ │ │ │ │ ├── parse-snippet.ts
│ │ │ │ │ ├── test-data/
│ │ │ │ │ │ └── snippet.ts
│ │ │ │ │ └── validation/
│ │ │ │ │ ├── index.ts
│ │ │ │ │ └── validation.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── interfaces/
│ │ │ │ │ ├── branch.interface.ts
│ │ │ │ │ ├── commit-info.interface.ts
│ │ │ │ │ ├── github-auth.interface.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── pull-request.intreface.ts
│ │ │ │ │ ├── repo.interface.ts
│ │ │ │ │ ├── snippet.ts
│ │ │ │ │ └── user.interface.ts
│ │ │ │ └── services/
│ │ │ │ ├── github.service.ts
│ │ │ │ ├── snippet.service.spec.ts
│ │ │ │ └── snippet.service.ts
│ │ │ ├── assets/
│ │ │ │ └── .gitkeep
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.scss
│ │ │ └── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── blog/
│ │ ├── browserslist
│ │ ├── jest.config.js
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.scss
│ │ │ │ ├── app.component.spec.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── common.ts
│ │ │ │ ├── feed/
│ │ │ │ │ ├── feed.component.html
│ │ │ │ │ ├── feed.component.scss
│ │ │ │ │ ├── feed.component.spec.ts
│ │ │ │ │ └── feed.component.ts
│ │ │ │ ├── form/
│ │ │ │ │ ├── form.component.html
│ │ │ │ │ ├── form.component.scss
│ │ │ │ │ ├── form.component.spec.ts
│ │ │ │ │ └── form.component.ts
│ │ │ │ ├── post/
│ │ │ │ │ ├── post.component.html
│ │ │ │ │ ├── post.component.scss
│ │ │ │ │ ├── post.component.spec.ts
│ │ │ │ │ └── post.component.ts
│ │ │ │ ├── post.service.ts
│ │ │ │ └── single-post/
│ │ │ │ ├── single-post.component.html
│ │ │ │ ├── single-post.component.scss
│ │ │ │ └── single-post.component.ts
│ │ │ ├── assets/
│ │ │ │ ├── .gitkeep
│ │ │ │ └── fonts/
│ │ │ │ └── droid-sans/
│ │ │ │ └── Apache License.txt
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.scss
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── codelab/
│ │ ├── browserslist
│ │ ├── extra-webpack.config.js
│ │ ├── karma.conf.js
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── admin/
│ │ │ │ │ ├── admin-routing.module.ts
│ │ │ │ │ ├── admin.component.css
│ │ │ │ │ ├── admin.component.html
│ │ │ │ │ ├── admin.component.spec.ts
│ │ │ │ │ ├── admin.component.ts
│ │ │ │ │ ├── admin.module.ts
│ │ │ │ │ ├── feedback/
│ │ │ │ │ │ ├── feedback-message-table/
│ │ │ │ │ │ │ ├── feedback-message-table.component.ts
│ │ │ │ │ │ │ ├── feedback-message-table.css
│ │ │ │ │ │ │ └── feedback-message-table.html
│ │ │ │ │ │ ├── feedback.component.css
│ │ │ │ │ │ ├── feedback.component.html
│ │ │ │ │ │ ├── feedback.component.spec.ts
│ │ │ │ │ │ ├── feedback.component.ts
│ │ │ │ │ │ ├── feedback.module.ts
│ │ │ │ │ │ └── github.service.ts
│ │ │ │ │ └── users/
│ │ │ │ │ ├── users.component.css
│ │ │ │ │ ├── users.component.html
│ │ │ │ │ ├── users.component.spec.ts
│ │ │ │ │ ├── users.component.ts
│ │ │ │ │ └── users.module.ts
│ │ │ │ ├── app-routing.module.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── codelabs/
│ │ │ │ │ ├── about/
│ │ │ │ │ │ ├── about.component.html
│ │ │ │ │ │ ├── about.component.scss
│ │ │ │ │ │ ├── about.component.spec.ts
│ │ │ │ │ │ ├── about.component.ts
│ │ │ │ │ │ ├── about.module.ts
│ │ │ │ │ │ └── samples/
│ │ │ │ │ │ ├── fake-types.d.ts.not-really
│ │ │ │ │ │ ├── slides/
│ │ │ │ │ │ │ ├── ng-template.html
│ │ │ │ │ │ │ ├── slide-component.html
│ │ │ │ │ │ │ └── structural-directive.html
│ │ │ │ │ │ └── storing-code/
│ │ │ │ │ │ ├── backticks.html
│ │ │ │ │ │ ├── interpolations.ts
│ │ │ │ │ │ └── plain.html
│ │ │ │ │ ├── angular/
│ │ │ │ │ │ ├── angular-cli/
│ │ │ │ │ │ │ ├── angular-cli.component.css
│ │ │ │ │ │ │ ├── angular-cli.component.html
│ │ │ │ │ │ │ ├── angular-cli.component.ts
│ │ │ │ │ │ │ └── angular-cli.module.ts
│ │ │ │ │ │ ├── angular-routing.module.ts
│ │ │ │ │ │ ├── angular.module.ts
│ │ │ │ │ │ ├── component-tree/
│ │ │ │ │ │ │ ├── component-tree.component.css
│ │ │ │ │ │ │ ├── component-tree.component.html
│ │ │ │ │ │ │ ├── component-tree.component.ts
│ │ │ │ │ │ │ ├── component-tree.module.ts
│ │ │ │ │ │ │ ├── components-hierarchy-svg/
│ │ │ │ │ │ │ │ ├── components-hierarchy-svg.component.html
│ │ │ │ │ │ │ │ ├── components-hierarchy-svg.component.spec.ts
│ │ │ │ │ │ │ │ ├── components-hierarchy-svg.component.ts
│ │ │ │ │ │ │ │ └── index.ts
│ │ │ │ │ │ │ └── samples/
│ │ │ │ │ │ │ └── module/
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ ├── box.component.ts
│ │ │ │ │ │ │ ├── circle.component.ts
│ │ │ │ │ │ │ └── index.html
│ │ │ │ │ │ ├── create-first-app/
│ │ │ │ │ │ │ ├── create-first-app.component.css
│ │ │ │ │ │ │ ├── create-first-app.component.html
│ │ │ │ │ │ │ ├── create-first-app.component.ts
│ │ │ │ │ │ │ ├── create-first-app.module.ts
│ │ │ │ │ │ │ ├── mode/
│ │ │ │ │ │ │ │ ├── mode.component.css
│ │ │ │ │ │ │ │ ├── mode.component.html
│ │ │ │ │ │ │ │ ├── mode.component.spec.ts
│ │ │ │ │ │ │ │ └── mode.component.ts
│ │ │ │ │ │ │ └── samples/
│ │ │ │ │ │ │ ├── app-component/
│ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ │ ├── bootstrap.ts
│ │ │ │ │ │ │ │ └── index.html
│ │ │ │ │ │ │ └── index-html/
│ │ │ │ │ │ │ ├── bootstrap.ts
│ │ │ │ │ │ │ └── index.html
│ │ │ │ │ │ ├── custom-events/
│ │ │ │ │ │ │ ├── custom-events.component.css
│ │ │ │ │ │ │ ├── custom-events.component.html
│ │ │ │ │ │ │ ├── custom-events.component.ts
│ │ │ │ │ │ │ └── custom-events.module.ts
│ │ │ │ │ │ ├── dependency-injection/
│ │ │ │ │ │ │ ├── dependency-injection.component.css
│ │ │ │ │ │ │ ├── dependency-injection.component.html
│ │ │ │ │ │ │ ├── dependency-injection.component.ts
│ │ │ │ │ │ │ └── dependency-injection.module.ts
│ │ │ │ │ │ ├── forms/
│ │ │ │ │ │ │ ├── forms.component.css
│ │ │ │ │ │ │ ├── forms.component.html
│ │ │ │ │ │ │ ├── forms.component.ts
│ │ │ │ │ │ │ ├── forms.module.ts
│ │ │ │ │ │ │ └── samples/
│ │ │ │ │ │ │ └── basic/
│ │ │ │ │ │ │ ├── app.1.html
│ │ │ │ │ │ │ ├── app.2.html
│ │ │ │ │ │ │ ├── app.3.html
│ │ │ │ │ │ │ ├── app.4.html
│ │ │ │ │ │ │ ├── app.5.html
│ │ │ │ │ │ │ ├── app.6.html
│ │ │ │ │ │ │ ├── app.component.5.ts
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ ├── app.module.6.ts
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ ├── code.ts
│ │ │ │ │ │ │ ├── main.ts
│ │ │ │ │ │ │ └── styles.css
│ │ │ │ │ │ ├── material/
│ │ │ │ │ │ │ ├── material.component.css
│ │ │ │ │ │ │ ├── material.component.html
│ │ │ │ │ │ │ ├── material.component.ts
│ │ │ │ │ │ │ ├── material.module.ts
│ │ │ │ │ │ │ └── samples/
│ │ │ │ │ │ │ ├── basic/
│ │ │ │ │ │ │ │ ├── app.1.html
│ │ │ │ │ │ │ │ ├── app.2.html
│ │ │ │ │ │ │ │ ├── app.3.html
│ │ │ │ │ │ │ │ ├── app.4.html
│ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ │ ├── code.ts
│ │ │ │ │ │ │ │ └── main.ts
│ │ │ │ │ │ │ ├── step1/
│ │ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ │ ├── step2/
│ │ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ │ ├── step3/
│ │ │ │ │ │ │ │ └── app.html
│ │ │ │ │ │ │ └── step4/
│ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ ├── pipes/
│ │ │ │ │ │ │ ├── pipes.component.css
│ │ │ │ │ │ │ ├── pipes.component.html
│ │ │ │ │ │ │ ├── pipes.component.ts
│ │ │ │ │ │ │ ├── pipes.module.ts
│ │ │ │ │ │ │ └── samples/
│ │ │ │ │ │ │ └── pipes/
│ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ ├── playground/
│ │ │ │ │ │ │ ├── angular-sample.ts
│ │ │ │ │ │ │ ├── playground.component.css
│ │ │ │ │ │ │ ├── playground.component.html
│ │ │ │ │ │ │ ├── playground.component.spec.ts
│ │ │ │ │ │ │ ├── playground.component.ts
│ │ │ │ │ │ │ └── playground.module.ts
│ │ │ │ │ │ ├── router/
│ │ │ │ │ │ │ ├── router.component.css
│ │ │ │ │ │ │ ├── router.component.html
│ │ │ │ │ │ │ ├── router.component.ts
│ │ │ │ │ │ │ ├── router.module.ts
│ │ │ │ │ │ │ └── samples/
│ │ │ │ │ │ │ └── simple-router/
│ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ ├── code.ts
│ │ │ │ │ │ │ ├── components/
│ │ │ │ │ │ │ │ ├── kitten.ts
│ │ │ │ │ │ │ │ └── puppy.ts
│ │ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ │ └── main.ts
│ │ │ │ │ │ ├── structural-directives/
│ │ │ │ │ │ │ ├── bsod.css
│ │ │ │ │ │ │ ├── samples/
│ │ │ │ │ │ │ │ ├── mat-tab-nav-bar/
│ │ │ │ │ │ │ │ │ ├── alert.component.ts
│ │ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ │ │ └── tab.component.ts
│ │ │ │ │ │ │ │ ├── material-tabs/
│ │ │ │ │ │ │ │ │ ├── alert.component.ts
│ │ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ │ │ ├── app.solved.html
│ │ │ │ │ │ │ │ │ ├── break-my-computer.component.ts
│ │ │ │ │ │ │ │ │ ├── style.css
│ │ │ │ │ │ │ │ │ └── taet-led.component.ts
│ │ │ │ │ │ │ │ ├── material-tabs-structural-directive/
│ │ │ │ │ │ │ │ │ ├── alert.component.ts
│ │ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ │ ├── app.html
│ │ │ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ │ │ ├── app.solved.html
│ │ │ │ │ │ │ │ │ ├── hideme.directive.solved.ts
│ │ │ │ │ │ │ │ │ ├── hideme.directive.ts
│ │ │ │ │ │ │ │ │ └── ignored.module.ts
│ │ │ │ │ │ │ │ ├── micro-syntax/
│ │ │ │ │ │ │ │ │ ├── code.ts
│ │ │ │ │ │ │ │ │ ├── ms.spec.ts
│ │ │ │ │ │ │ │ │ └── ms.ts
│ │ │ │ │ │ │ │ └── structural-directives/
│ │ │ │ │ │ │ │ ├── microsyntax.html
│ │ │ │ │ │ │ │ ├── ng-for-after.html
│ │ │ │ │ │ │ │ ├── ng-for-before.html
│ │ │ │ │ │ │ │ ├── ng-if-after.html
│ │ │ │ │ │ │ │ └── ng-if-before.html
│ │ │ │ │ │ │ ├── structural-directives.component.css
│ │ │ │ │ │ │ ├── structural-directives.component.html
│ │ │ │ │ │ │ ├── structural-directives.component.ts
│ │ │ │ │ │ │ └── structural-directives.module.ts
│ │ │ │ │ │ ├── templates/
│ │ │ │ │ │ │ ├── samples/
│ │ │ │ │ │ │ │ ├── data-binding-extra/
│ │ │ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ │ │ │ └── number-praiser.ts
│ │ │ │ │ │ │ │ ├── event-binding/
│ │ │ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ │ │ ├── event-binding-shortcuts/
│ │ │ │ │ │ │ │ │ └── app.component.html
│ │ │ │ │ │ │ │ └── reference-binding/
│ │ │ │ │ │ │ │ ├── app.component.html
│ │ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ │ └── app.module.ts
│ │ │ │ │ │ │ ├── templates.component.css
│ │ │ │ │ │ │ ├── templates.component.html
│ │ │ │ │ │ │ ├── templates.component.ts
│ │ │ │ │ │ │ └── templates.module.ts
│ │ │ │ │ │ └── typescript/
│ │ │ │ │ │ ├── typescript/
│ │ │ │ │ │ │ ├── code/
│ │ │ │ │ │ │ │ ├── app.ts
│ │ │ │ │ │ │ │ ├── code.ts
│ │ │ │ │ │ │ │ └── mini-exercise-test.ts
│ │ │ │ │ │ │ ├── typescript-svg/
│ │ │ │ │ │ │ │ ├── typescript-svg.component.html
│ │ │ │ │ │ │ │ ├── typescript-svg.component.spec.ts
│ │ │ │ │ │ │ │ └── typescript-svg.component.ts
│ │ │ │ │ │ │ ├── typescript.component.css
│ │ │ │ │ │ │ ├── typescript.component.html
│ │ │ │ │ │ │ └── typescript.component.ts
│ │ │ │ │ │ ├── typescript-routing.module.ts
│ │ │ │ │ │ └── typescript.module.ts
│ │ │ │ │ ├── codelabs-routing.module.ts
│ │ │ │ │ ├── codelabs.module.ts
│ │ │ │ │ └── extra/
│ │ │ │ │ ├── code-playground/
│ │ │ │ │ │ ├── code-playground.component.css
│ │ │ │ │ │ ├── code-playground.component.html
│ │ │ │ │ │ ├── code-playground.component.ts
│ │ │ │ │ │ └── code-playground.module.ts
│ │ │ │ │ ├── extra-routing.module.ts
│ │ │ │ │ ├── extra.module.ts
│ │ │ │ │ ├── rating-summary/
│ │ │ │ │ │ ├── rating-summary.component.css
│ │ │ │ │ │ ├── rating-summary.component.html
│ │ │ │ │ │ ├── rating-summary.component.ts
│ │ │ │ │ │ └── rating-summary.module.ts
│ │ │ │ │ └── visual-studio-code/
│ │ │ │ │ ├── visual-studio-code.component.css
│ │ │ │ │ ├── visual-studio-code.component.html
│ │ │ │ │ ├── visual-studio-code.component.ts
│ │ │ │ │ └── visual-studio-code.module.ts
│ │ │ │ ├── common.ts
│ │ │ │ ├── components/
│ │ │ │ │ ├── angular-routes/
│ │ │ │ │ │ ├── angular-routes.component.html
│ │ │ │ │ │ ├── angular-routes.component.scss
│ │ │ │ │ │ ├── angular-routes.component.ts
│ │ │ │ │ │ └── angular-routes.module.ts
│ │ │ │ │ ├── angular-test-runner/
│ │ │ │ │ │ ├── angular-test-runner.component.css
│ │ │ │ │ │ ├── angular-test-runner.component.html
│ │ │ │ │ │ ├── angular-test-runner.component.ts
│ │ │ │ │ │ └── tests.ts
│ │ │ │ │ ├── babel-test-runner/
│ │ │ │ │ │ ├── babel-helpers.ts
│ │ │ │ │ │ ├── babel-test-runner.component.css
│ │ │ │ │ │ ├── babel-test-runner.component.html
│ │ │ │ │ │ └── babel-test-runner.component.ts
│ │ │ │ │ ├── breadcrumb/
│ │ │ │ │ │ ├── breadcrumb.component.css
│ │ │ │ │ │ ├── breadcrumb.component.html
│ │ │ │ │ │ ├── breadcrumb.component.spec.ts
│ │ │ │ │ │ └── breadcrumb.component.ts
│ │ │ │ │ ├── buttons-nav-bar/
│ │ │ │ │ │ ├── buttons-nav-bar.component.html
│ │ │ │ │ │ ├── buttons-nav-bar.component.scss
│ │ │ │ │ │ ├── buttons-nav-bar.component.ts
│ │ │ │ │ │ ├── buttons-nav-bar.module.ts
│ │ │ │ │ │ ├── menu-fullscreen-widget/
│ │ │ │ │ │ │ ├── menu-fullscreen-widget.component.html
│ │ │ │ │ │ │ ├── menu-fullscreen-widget.component.scss
│ │ │ │ │ │ │ ├── menu-fullscreen-widget.component.spec.ts
│ │ │ │ │ │ │ └── menu-fullscreen-widget.component.ts
│ │ │ │ │ │ ├── menu-github-widget/
│ │ │ │ │ │ │ ├── menu-github-widget.component.css
│ │ │ │ │ │ │ ├── menu-github-widget.component.html
│ │ │ │ │ │ │ ├── menu-github-widget.component.ts
│ │ │ │ │ │ │ └── menu-github-widget.module.ts
│ │ │ │ │ │ └── menu-shortcut-widget/
│ │ │ │ │ │ ├── menu-shortcut-widget.component.css
│ │ │ │ │ │ ├── menu-shortcut-widget.component.html
│ │ │ │ │ │ ├── menu-shortcut-widget.component.ts
│ │ │ │ │ │ └── menu-shortcut-widget.module.ts
│ │ │ │ │ ├── codelab-components.module.ts
│ │ │ │ │ ├── codelab-progress-bar/
│ │ │ │ │ │ ├── codelab-progress-bar.component.css
│ │ │ │ │ │ ├── codelab-progress-bar.component.html
│ │ │ │ │ │ └── codelab-progress-bar.component.ts
│ │ │ │ │ ├── css/
│ │ │ │ │ │ └── codelab-styles.scss
│ │ │ │ │ ├── exercise/
│ │ │ │ │ │ ├── exercise.component.css
│ │ │ │ │ │ ├── exercise.component.html
│ │ │ │ │ │ └── exercise.component.ts
│ │ │ │ │ ├── exercise-playground/
│ │ │ │ │ │ ├── codelab-exercise-playground.component.css
│ │ │ │ │ │ ├── codelab-exercise-playground.component.html
│ │ │ │ │ │ └── codelab-exercise-playground.component.ts
│ │ │ │ │ ├── exercise-preview/
│ │ │ │ │ │ ├── exercise-preview.component.html
│ │ │ │ │ │ └── exercise-preview.component.ts
│ │ │ │ │ ├── external-link-directive/
│ │ │ │ │ │ ├── external-link-directive.directive.spec.ts
│ │ │ │ │ │ └── external-link-directive.directive.ts
│ │ │ │ │ ├── index/
│ │ │ │ │ │ ├── index.component.html
│ │ │ │ │ │ ├── index.component.scss
│ │ │ │ │ │ ├── index.component.ts
│ │ │ │ │ │ └── index.module.ts
│ │ │ │ │ ├── login/
│ │ │ │ │ │ ├── login.component.css
│ │ │ │ │ │ ├── login.component.html
│ │ │ │ │ │ ├── login.component.spec.ts
│ │ │ │ │ │ ├── login.component.ts
│ │ │ │ │ │ └── login.module.ts
│ │ │ │ │ ├── not-found/
│ │ │ │ │ │ ├── not-found.component.html
│ │ │ │ │ │ ├── not-found.component.scss
│ │ │ │ │ │ ├── not-found.component.ts
│ │ │ │ │ │ └── not-found.module.ts
│ │ │ │ │ ├── slides/
│ │ │ │ │ │ ├── closing-slide/
│ │ │ │ │ │ │ ├── codelab-closing-slide.component.css
│ │ │ │ │ │ │ ├── codelab-closing-slide.component.html
│ │ │ │ │ │ │ ├── codelab-closing-slide.component.spec.ts
│ │ │ │ │ │ │ └── codelab-closing-slide.component.ts
│ │ │ │ │ │ └── title-slide/
│ │ │ │ │ │ ├── ripple-animation/
│ │ │ │ │ │ │ ├── codelab-ripple-animation.component.css
│ │ │ │ │ │ │ ├── codelab-ripple-animation.component.html
│ │ │ │ │ │ │ ├── codelab-ripple-animation.component.spec.ts
│ │ │ │ │ │ │ └── codelab-ripple-animation.component.ts
│ │ │ │ │ │ ├── title-slide.component.css
│ │ │ │ │ │ ├── title-slide.component.html
│ │ │ │ │ │ ├── title-slide.component.spec.ts
│ │ │ │ │ │ └── title-slide.component.ts
│ │ │ │ │ └── slides-preview/
│ │ │ │ │ ├── codelab-preview.component.html
│ │ │ │ │ ├── codelab-preview.component.scss
│ │ │ │ │ └── codelab-preview.component.ts
│ │ │ │ ├── containers/
│ │ │ │ │ ├── full-layout/
│ │ │ │ │ │ ├── full-layout.component.html
│ │ │ │ │ │ ├── full-layout.component.scss
│ │ │ │ │ │ ├── full-layout.component.ts
│ │ │ │ │ │ ├── full-layout.module.ts
│ │ │ │ │ │ └── index.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── directives/
│ │ │ │ │ ├── directives.module.ts
│ │ │ │ │ ├── nextSlide.directive.ts
│ │ │ │ │ ├── permissions/
│ │ │ │ │ │ ├── abstract-permission.ts
│ │ │ │ │ │ ├── can-load-admin/
│ │ │ │ │ │ │ └── can-load-admin.directive.ts
│ │ │ │ │ │ └── is-logged-in/
│ │ │ │ │ │ └── is-loggef-in.directive.ts
│ │ │ │ │ └── previousSlide.directive.ts
│ │ │ │ ├── shared/
│ │ │ │ │ ├── angular-code/
│ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ ├── bootstrap.ts
│ │ │ │ │ │ ├── code.ts
│ │ │ │ │ │ └── index.html
│ │ │ │ │ ├── helpers/
│ │ │ │ │ │ ├── codelabFile.ts
│ │ │ │ │ │ └── helpers.ts
│ │ │ │ │ ├── interfaces/
│ │ │ │ │ │ ├── exercise-config.ts
│ │ │ │ │ │ ├── file-config.ts
│ │ │ │ │ │ └── test-info.ts
│ │ │ │ │ ├── services/
│ │ │ │ │ │ ├── access.service.ts
│ │ │ │ │ │ └── guards/
│ │ │ │ │ │ ├── admin-guard.ts
│ │ │ │ │ │ └── login-guard.ts
│ │ │ │ │ └── shared.module.ts
│ │ │ │ └── sync/
│ │ │ │ ├── sync.component.css
│ │ │ │ ├── sync.component.html
│ │ │ │ ├── sync.component.spec.ts
│ │ │ │ ├── sync.component.ts
│ │ │ │ └── sync.module.ts
│ │ │ ├── assets/
│ │ │ │ └── .gitkeep
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── locale/
│ │ │ │ ├── codelab.ru.xtb
│ │ │ │ └── messages.xmb
│ │ │ ├── main.ts
│ │ │ ├── manifest.webmanifest
│ │ │ ├── polyfills.ts
│ │ │ ├── service-worker.js
│ │ │ ├── styles.scss
│ │ │ ├── test.ts
│ │ │ └── typings.d.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── kirjs/
│ │ ├── browserslist
│ │ ├── karma.conf.js
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── app.component.css
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.spec.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── cv/
│ │ │ │ │ ├── resume.css
│ │ │ │ │ ├── resume.html
│ │ │ │ │ └── resume.scss
│ │ │ │ ├── kirjs.module.ts
│ │ │ │ └── modules/
│ │ │ │ ├── ast/
│ │ │ │ │ ├── ast-preview-runner/
│ │ │ │ │ │ ├── ast-preview-runner.component.css
│ │ │ │ │ │ ├── ast-preview-runner.component.html
│ │ │ │ │ │ ├── ast-preview-runner.component.ts
│ │ │ │ │ │ └── ast-preview-runner.module.ts
│ │ │ │ │ ├── ast.component.css
│ │ │ │ │ ├── ast.component.html
│ │ │ │ │ ├── ast.component.ts
│ │ │ │ │ ├── ast.module.ts
│ │ │ │ │ ├── babel-highlight/
│ │ │ │ │ │ └── babel-highlight-match.directive.ts
│ │ │ │ │ ├── debugger/
│ │ │ │ │ │ ├── debugger.component.css
│ │ │ │ │ │ ├── debugger.component.html
│ │ │ │ │ │ ├── debugger.component.spec.ts
│ │ │ │ │ │ ├── debugger.component.ts
│ │ │ │ │ │ └── debugger.ts
│ │ │ │ │ ├── new-progress-bar/
│ │ │ │ │ │ ├── new-progress-bar.component.css
│ │ │ │ │ │ ├── new-progress-bar.component.html
│ │ │ │ │ │ ├── new-progress-bar.component.ts
│ │ │ │ │ │ └── new-progress-bar.module.ts
│ │ │ │ │ ├── parse-hello-world-ast.ts
│ │ │ │ │ ├── samples/
│ │ │ │ │ │ ├── dec-to-bin-with-semicolons.js
│ │ │ │ │ │ ├── dec-to-bin.js
│ │ │ │ │ │ ├── eslint/
│ │ │ │ │ │ │ ├── eslint.js
│ │ │ │ │ │ │ └── eslint.test.js
│ │ │ │ │ │ ├── find-console-log/
│ │ │ │ │ │ │ ├── find-console-log-babel.solved.ts
│ │ │ │ │ │ │ ├── find-console-log-babel.ts
│ │ │ │ │ │ │ ├── find-console-log-regex.solved.js
│ │ │ │ │ │ │ ├── find-console-log.js
│ │ │ │ │ │ │ ├── find-console-log.test.js
│ │ │ │ │ │ │ ├── remove-console-log.solved.ts
│ │ │ │ │ │ │ ├── remove-console-log.test.js
│ │ │ │ │ │ │ ├── remove-console-log.ts
│ │ │ │ │ │ │ ├── traverse-console-log-babel.solved.ts
│ │ │ │ │ │ │ ├── traverse-console-log-babel.solved2.ts
│ │ │ │ │ │ │ └── traverse-console-log-babel.ts
│ │ │ │ │ │ ├── find-debugger/
│ │ │ │ │ │ │ ├── find-debugger-babel.solved.ts
│ │ │ │ │ │ │ ├── find-debugger-babel.ts
│ │ │ │ │ │ │ ├── find-debugger-regex.solved.js
│ │ │ │ │ │ │ ├── find-debugger.js
│ │ │ │ │ │ │ ├── find-debugger.test.js
│ │ │ │ │ │ │ ├── hint.js
│ │ │ │ │ │ │ ├── remove-debugger.solved.ts
│ │ │ │ │ │ │ ├── remove-debugger.test.js
│ │ │ │ │ │ │ ├── remove-debugger.ts
│ │ │ │ │ │ │ ├── traverse-debugger-babel.solved.ts
│ │ │ │ │ │ │ └── traverse-debugger-babel.ts
│ │ │ │ │ │ ├── find-fit/
│ │ │ │ │ │ │ ├── find-fit.js
│ │ │ │ │ │ │ ├── find-fit.solved.js
│ │ │ │ │ │ │ └── find-fit.test.js
│ │ │ │ │ │ ├── hello-world.json
│ │ │ │ │ │ ├── it-lines/
│ │ │ │ │ │ │ ├── it-lines.js
│ │ │ │ │ │ │ ├── it-lines.solved.js
│ │ │ │ │ │ │ └── it-lines.test.js
│ │ │ │ │ │ └── tricky.js
│ │ │ │ │ ├── size-picker/
│ │ │ │ │ │ ├── size-picker.component.css
│ │ │ │ │ │ ├── size-picker.component.html
│ │ │ │ │ │ ├── size-picker.component.spec.ts
│ │ │ │ │ │ ├── size-picker.component.ts
│ │ │ │ │ │ └── size-picker.module.ts
│ │ │ │ │ └── test-set/
│ │ │ │ │ ├── babel-test-runner/
│ │ │ │ │ │ ├── babel-test-runner.component.css
│ │ │ │ │ │ ├── babel-test-runner.component.html
│ │ │ │ │ │ └── babel-test-runner.component.ts
│ │ │ │ │ ├── test-set.component.css
│ │ │ │ │ ├── test-set.component.html
│ │ │ │ │ ├── test-set.component.spec.ts
│ │ │ │ │ └── test-set.component.ts
│ │ │ │ ├── binary/
│ │ │ │ │ ├── angular-flags/
│ │ │ │ │ │ ├── angular-flags.component.css
│ │ │ │ │ │ ├── angular-flags.component.html
│ │ │ │ │ │ ├── angular-flags.component.spec.ts
│ │ │ │ │ │ └── angular-flags.component.ts
│ │ │ │ │ ├── ascii/
│ │ │ │ │ │ ├── ascii.component.css
│ │ │ │ │ │ ├── ascii.component.html
│ │ │ │ │ │ ├── ascii.component.spec.ts
│ │ │ │ │ │ └── ascii.component.ts
│ │ │ │ │ ├── binary-flat/
│ │ │ │ │ │ ├── binary-flat.component.css
│ │ │ │ │ │ ├── binary-flat.component.html
│ │ │ │ │ │ ├── binary-flat.component.spec.ts
│ │ │ │ │ │ └── binary-flat.component.ts
│ │ │ │ │ ├── binary-gif/
│ │ │ │ │ │ ├── binary-gif.component.css
│ │ │ │ │ │ ├── binary-gif.component.html
│ │ │ │ │ │ ├── binary-gif.component.spec.ts
│ │ │ │ │ │ └── binary-gif.component.ts
│ │ │ │ │ ├── binary-inline/
│ │ │ │ │ │ ├── binary-display/
│ │ │ │ │ │ │ ├── binary-display.component.css
│ │ │ │ │ │ │ ├── binary-display.component.html
│ │ │ │ │ │ │ ├── binary-display.component.spec.ts
│ │ │ │ │ │ │ └── binary-display.component.ts
│ │ │ │ │ │ ├── binary-inline.component.css
│ │ │ │ │ │ ├── binary-inline.component.html
│ │ │ │ │ │ ├── binary-inline.component.spec.ts
│ │ │ │ │ │ ├── binary-inline.component.ts
│ │ │ │ │ │ └── binary-inline.module.ts
│ │ │ │ │ ├── binary-parser-demo/
│ │ │ │ │ │ ├── binary-parser-demo.component.css
│ │ │ │ │ │ ├── binary-parser-demo.component.html
│ │ │ │ │ │ ├── binary-parser-demo.component.spec.ts
│ │ │ │ │ │ └── binary-parser-demo.component.ts
│ │ │ │ │ ├── binary-plain/
│ │ │ │ │ │ ├── binary-plain.component.css
│ │ │ │ │ │ ├── binary-plain.component.html
│ │ │ │ │ │ ├── binary-plain.component.spec.ts
│ │ │ │ │ │ └── binary-plain.component.ts
│ │ │ │ │ ├── binary-view/
│ │ │ │ │ │ ├── array/
│ │ │ │ │ │ │ ├── array.component.css
│ │ │ │ │ │ │ ├── array.component.html
│ │ │ │ │ │ │ ├── array.component.spec.ts
│ │ │ │ │ │ │ └── array.component.ts
│ │ │ │ │ │ ├── binary-parent/
│ │ │ │ │ │ │ ├── binary-parent.component.html
│ │ │ │ │ │ │ ├── binary-parent.component.scss
│ │ │ │ │ │ │ ├── binary-parent.component.spec.ts
│ │ │ │ │ │ │ └── binary-parent.component.ts
│ │ │ │ │ │ ├── binary-view.module.ts
│ │ │ │ │ │ ├── bits/
│ │ │ │ │ │ │ ├── bits.component.css
│ │ │ │ │ │ │ ├── bits.component.html
│ │ │ │ │ │ │ ├── bits.component.spec.ts
│ │ │ │ │ │ │ └── bits.component.ts
│ │ │ │ │ │ ├── block/
│ │ │ │ │ │ │ ├── block.component.css
│ │ │ │ │ │ │ ├── block.component.html
│ │ │ │ │ │ │ ├── block.component.spec.ts
│ │ │ │ │ │ │ └── block.component.ts
│ │ │ │ │ │ ├── color/
│ │ │ │ │ │ │ ├── color.component.css
│ │ │ │ │ │ │ ├── color.component.html
│ │ │ │ │ │ │ ├── color.component.spec.ts
│ │ │ │ │ │ │ └── color.component.ts
│ │ │ │ │ │ ├── hex/
│ │ │ │ │ │ │ ├── hex.component.css
│ │ │ │ │ │ │ ├── hex.component.html
│ │ │ │ │ │ │ ├── hex.component.spec.ts
│ │ │ │ │ │ │ └── hex.component.ts
│ │ │ │ │ │ ├── inline/
│ │ │ │ │ │ │ ├── inline.component.css
│ │ │ │ │ │ │ ├── inline.component.html
│ │ │ │ │ │ │ ├── inline.component.spec.ts
│ │ │ │ │ │ │ └── inline.component.ts
│ │ │ │ │ │ ├── inline-root/
│ │ │ │ │ │ │ ├── inline-root.component.css
│ │ │ │ │ │ │ ├── inline-root.component.html
│ │ │ │ │ │ │ ├── inline-root.component.spec.ts
│ │ │ │ │ │ │ └── inline-root.component.ts
│ │ │ │ │ │ ├── number/
│ │ │ │ │ │ │ ├── number.component.css
│ │ │ │ │ │ │ ├── number.component.html
│ │ │ │ │ │ │ ├── number.component.spec.ts
│ │ │ │ │ │ │ └── number.component.ts
│ │ │ │ │ │ ├── object/
│ │ │ │ │ │ │ ├── object.component.css
│ │ │ │ │ │ │ ├── object.component.html
│ │ │ │ │ │ │ ├── object.component.spec.ts
│ │ │ │ │ │ │ └── object.component.ts
│ │ │ │ │ │ └── string/
│ │ │ │ │ │ ├── string.component.css
│ │ │ │ │ │ ├── string.component.html
│ │ │ │ │ │ ├── string.component.spec.ts
│ │ │ │ │ │ └── string.component.ts
│ │ │ │ │ ├── binary.component.html
│ │ │ │ │ ├── binary.component.scss
│ │ │ │ │ ├── binary.component.ts
│ │ │ │ │ ├── binary.module.ts
│ │ │ │ │ ├── bindec/
│ │ │ │ │ │ ├── bindec.component.css
│ │ │ │ │ │ ├── bindec.component.html
│ │ │ │ │ │ ├── bindec.component.spec.ts
│ │ │ │ │ │ └── bindec.component.ts
│ │ │ │ │ ├── bit/
│ │ │ │ │ │ ├── bit.component.css
│ │ │ │ │ │ ├── bit.component.html
│ │ │ │ │ │ ├── bit.component.spec.ts
│ │ │ │ │ │ └── bit.component.ts
│ │ │ │ │ ├── bitwise/
│ │ │ │ │ │ ├── bitwise.component.css
│ │ │ │ │ │ ├── bitwise.component.html
│ │ │ │ │ │ ├── bitwise.component.spec.ts
│ │ │ │ │ │ └── bitwise.component.ts
│ │ │ │ │ ├── color-indexing/
│ │ │ │ │ │ ├── color-indexing.component.css
│ │ │ │ │ │ ├── color-indexing.component.html
│ │ │ │ │ │ ├── color-indexing.component.spec.ts
│ │ │ │ │ │ └── color-indexing.component.ts
│ │ │ │ │ ├── compare/
│ │ │ │ │ │ ├── compare.component.css
│ │ │ │ │ │ ├── compare.component.html
│ │ │ │ │ │ ├── compare.component.spec.ts
│ │ │ │ │ │ └── compare.component.ts
│ │ │ │ │ ├── fake-gif/
│ │ │ │ │ │ ├── fake-gif.component.css
│ │ │ │ │ │ ├── fake-gif.component.html
│ │ │ │ │ │ ├── fake-gif.component.spec.ts
│ │ │ │ │ │ ├── fake-gif.component.ts
│ │ │ │ │ │ ├── gif-parser.ts
│ │ │ │ │ │ └── gif.ts
│ │ │ │ │ ├── gif-palette/
│ │ │ │ │ │ ├── gif-palette.component.css
│ │ │ │ │ │ ├── gif-palette.component.html
│ │ │ │ │ │ ├── gif-palette.component.spec.ts
│ │ │ │ │ │ └── gif-palette.component.ts
│ │ │ │ │ ├── hexdec/
│ │ │ │ │ │ ├── hexdec.component.css
│ │ │ │ │ │ ├── hexdec.component.html
│ │ │ │ │ │ ├── hexdec.component.spec.ts
│ │ │ │ │ │ └── hexdec.component.ts
│ │ │ │ │ ├── html-post/
│ │ │ │ │ │ ├── html-post.component.css
│ │ │ │ │ │ ├── html-post.component.html
│ │ │ │ │ │ ├── html-post.component.spec.ts
│ │ │ │ │ │ └── html-post.component.ts
│ │ │ │ │ ├── json/
│ │ │ │ │ │ ├── json.component.html
│ │ │ │ │ │ ├── json.component.scss
│ │ │ │ │ │ ├── json.component.spec.ts
│ │ │ │ │ │ └── json.component.ts
│ │ │ │ │ ├── memory/
│ │ │ │ │ │ ├── memory.component.css
│ │ │ │ │ │ ├── memory.component.html
│ │ │ │ │ │ ├── memory.component.spec.ts
│ │ │ │ │ │ └── memory.component.ts
│ │ │ │ │ ├── message/
│ │ │ │ │ │ ├── message.component.css
│ │ │ │ │ │ ├── message.component.html
│ │ │ │ │ │ ├── message.component.spec.ts
│ │ │ │ │ │ └── message.component.ts
│ │ │ │ │ ├── midi/
│ │ │ │ │ │ ├── midi.component.css
│ │ │ │ │ │ ├── midi.component.html
│ │ │ │ │ │ ├── midi.component.spec.ts
│ │ │ │ │ │ └── midi.component.ts
│ │ │ │ │ ├── parser/
│ │ │ │ │ │ ├── binary-parser.spec.ts
│ │ │ │ │ │ ├── binary-parser.ts
│ │ │ │ │ │ ├── parsers/
│ │ │ │ │ │ │ ├── abstract-parser.ts
│ │ │ │ │ │ │ ├── array-parser.spec.ts
│ │ │ │ │ │ │ ├── array-parser.ts
│ │ │ │ │ │ │ ├── bit-parser.spec.ts
│ │ │ │ │ │ │ ├── bit-parser.ts
│ │ │ │ │ │ │ ├── choice-parser.spec.ts
│ │ │ │ │ │ │ ├── choice-parser.ts
│ │ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ │ ├── debugger-parser.ts
│ │ │ │ │ │ │ ├── first-bit-parser.spec.ts
│ │ │ │ │ │ │ ├── object-parser.spec.ts
│ │ │ │ │ │ │ ├── object-parser.ts
│ │ │ │ │ │ │ ├── string-parser.spec.ts
│ │ │ │ │ │ │ ├── string-parser.ts
│ │ │ │ │ │ │ └── var-uint-parser.ts
│ │ │ │ │ │ ├── readers/
│ │ │ │ │ │ │ ├── abstract-reader.ts
│ │ │ │ │ │ │ └── string-reader.ts
│ │ │ │ │ │ └── utils.ts
│ │ │ │ │ ├── shared.ts
│ │ │ │ │ └── to-read/
│ │ │ │ │ ├── to-read.component.css
│ │ │ │ │ ├── to-read.component.html
│ │ │ │ │ ├── to-read.component.spec.ts
│ │ │ │ │ └── to-read.component.ts
│ │ │ │ ├── cellular-automation/
│ │ │ │ │ ├── board/
│ │ │ │ │ │ ├── board.component.css
│ │ │ │ │ │ ├── board.component.html
│ │ │ │ │ │ ├── board.component.spec.ts
│ │ │ │ │ │ └── board.component.ts
│ │ │ │ │ ├── cellular-automation-routing.module.ts
│ │ │ │ │ ├── cellular-automation.component.css
│ │ │ │ │ ├── cellular-automation.component.html
│ │ │ │ │ ├── cellular-automation.component.ts
│ │ │ │ │ ├── cellular-automation.module.ts
│ │ │ │ │ ├── oscilators/
│ │ │ │ │ │ ├── oscilators.component.css
│ │ │ │ │ │ ├── oscilators.component.html
│ │ │ │ │ │ ├── oscilators.component.spec.ts
│ │ │ │ │ │ └── oscilators.component.ts
│ │ │ │ │ ├── rule/
│ │ │ │ │ │ ├── rule.component.css
│ │ │ │ │ │ ├── rule.component.html
│ │ │ │ │ │ ├── rule.component.spec.ts
│ │ │ │ │ │ └── rule.component.ts
│ │ │ │ │ ├── rule3/
│ │ │ │ │ │ ├── rule3.component.css
│ │ │ │ │ │ ├── rule3.component.html
│ │ │ │ │ │ ├── rule3.component.ts
│ │ │ │ │ │ └── rule4/
│ │ │ │ │ │ ├── rule4.component.css
│ │ │ │ │ │ ├── rule4.component.html
│ │ │ │ │ │ ├── rule4.component.spec.ts
│ │ │ │ │ │ └── rule4.component.ts
│ │ │ │ │ └── rule8/
│ │ │ │ │ ├── rule8.component.css
│ │ │ │ │ ├── rule8.component.html
│ │ │ │ │ └── rule8.component.ts
│ │ │ │ ├── gomoku/
│ │ │ │ │ ├── board/
│ │ │ │ │ │ ├── board.component.html
│ │ │ │ │ │ ├── board.component.scss
│ │ │ │ │ │ ├── board.component.spec.ts
│ │ │ │ │ │ ├── board.component.ts
│ │ │ │ │ │ └── board.module.ts
│ │ │ │ │ ├── gomoku.component.css
│ │ │ │ │ ├── gomoku.component.html
│ │ │ │ │ ├── gomoku.component.ts
│ │ │ │ │ ├── gomoku.module.ts
│ │ │ │ │ ├── highlights.spec.ts
│ │ │ │ │ ├── highlights.ts
│ │ │ │ │ ├── renlib/
│ │ │ │ │ │ ├── I7.lib
│ │ │ │ │ │ ├── lines.lib
│ │ │ │ │ │ ├── moves.json
│ │ │ │ │ │ ├── parse.js
│ │ │ │ │ │ ├── ss.lib
│ │ │ │ │ │ └── test.lib
│ │ │ │ │ └── tools/
│ │ │ │ │ ├── tools.component.css
│ │ │ │ │ ├── tools.component.html
│ │ │ │ │ ├── tools.component.spec.ts
│ │ │ │ │ └── tools.component.ts
│ │ │ │ ├── gomoku-print/
│ │ │ │ │ ├── gomoku-print.component.css
│ │ │ │ │ ├── gomoku-print.component.html
│ │ │ │ │ ├── gomoku-print.component.ts
│ │ │ │ │ ├── gomoku-print.module.ts
│ │ │ │ │ ├── o/
│ │ │ │ │ │ ├── o.component.css
│ │ │ │ │ │ ├── o.component.html
│ │ │ │ │ │ ├── o.component.spec.ts
│ │ │ │ │ │ └── o.component.ts
│ │ │ │ │ └── x/
│ │ │ │ │ ├── x.component.css
│ │ │ │ │ ├── x.component.html
│ │ │ │ │ ├── x.component.spec.ts
│ │ │ │ │ └── x.component.ts
│ │ │ │ ├── home/
│ │ │ │ │ ├── home.component.css
│ │ │ │ │ ├── home.component.html
│ │ │ │ │ ├── home.component.spec.ts
│ │ │ │ │ ├── home.component.ts
│ │ │ │ │ ├── home.module.spec.ts
│ │ │ │ │ ├── home.module.ts
│ │ │ │ │ └── polaroid/
│ │ │ │ │ ├── polaroid.component.css
│ │ │ │ │ ├── polaroid.component.html
│ │ │ │ │ ├── polaroid.component.spec.ts
│ │ │ │ │ └── polaroid.component.ts
│ │ │ │ ├── msk/
│ │ │ │ │ ├── msk.component.css
│ │ │ │ │ ├── msk.component.html
│ │ │ │ │ ├── msk.component.spec.ts
│ │ │ │ │ ├── msk.component.ts
│ │ │ │ │ └── msk.module.ts
│ │ │ │ ├── music/
│ │ │ │ │ ├── music.component.css
│ │ │ │ │ ├── music.component.html
│ │ │ │ │ ├── music.component.spec.ts
│ │ │ │ │ ├── music.component.ts
│ │ │ │ │ └── music.module.ts
│ │ │ │ ├── qna/
│ │ │ │ │ ├── qna.component.css
│ │ │ │ │ ├── qna.component.html
│ │ │ │ │ ├── qna.component.spec.ts
│ │ │ │ │ ├── qna.component.ts
│ │ │ │ │ └── qna.module.ts
│ │ │ │ ├── regex/
│ │ │ │ │ ├── live/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── live-mock/
│ │ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ │ ├── live-mock.component.css
│ │ │ │ │ │ │ ├── live-mock.component.html
│ │ │ │ │ │ │ ├── live-mock.component.spec.ts
│ │ │ │ │ │ │ ├── live-mock.component.ts
│ │ │ │ │ │ │ └── live-mock.module.ts
│ │ │ │ │ │ ├── live.module.ts
│ │ │ │ │ │ ├── live.service.spec.ts
│ │ │ │ │ │ ├── live.service.ts
│ │ │ │ │ │ └── poll/
│ │ │ │ │ │ ├── index.ts
│ │ │ │ │ │ ├── poll.component.css
│ │ │ │ │ │ ├── poll.component.html
│ │ │ │ │ │ ├── poll.component.spec.ts
│ │ │ │ │ │ ├── poll.component.ts
│ │ │ │ │ │ └── poll.module.ts
│ │ │ │ │ ├── regex.component.css
│ │ │ │ │ ├── regex.component.html
│ │ │ │ │ ├── regex.component.spec.ts
│ │ │ │ │ ├── regex.component.ts
│ │ │ │ │ └── regex.module.ts
│ │ │ │ ├── stack/
│ │ │ │ │ ├── simple-stack/
│ │ │ │ │ │ ├── simple-stack.component.css
│ │ │ │ │ │ ├── simple-stack.component.html
│ │ │ │ │ │ ├── simple-stack.component.spec.ts
│ │ │ │ │ │ └── simple-stack.component.ts
│ │ │ │ │ ├── stack-game/
│ │ │ │ │ │ ├── stack-function/
│ │ │ │ │ │ │ ├── stack-function.component.css
│ │ │ │ │ │ │ ├── stack-function.component.html
│ │ │ │ │ │ │ ├── stack-function.component.spec.ts
│ │ │ │ │ │ │ └── stack-function.component.ts
│ │ │ │ │ │ ├── stack-function-button/
│ │ │ │ │ │ │ ├── stack-function-button.component.css
│ │ │ │ │ │ │ ├── stack-function-button.component.html
│ │ │ │ │ │ │ ├── stack-function-button.component.spec.ts
│ │ │ │ │ │ │ └── stack-function-button.component.ts
│ │ │ │ │ │ ├── stack-game.component.css
│ │ │ │ │ │ ├── stack-game.component.html
│ │ │ │ │ │ ├── stack-game.component.spec.ts
│ │ │ │ │ │ └── stack-game.component.ts
│ │ │ │ │ ├── stack-routing.module.ts
│ │ │ │ │ ├── stack-test/
│ │ │ │ │ │ ├── stack-test.component.html
│ │ │ │ │ │ ├── stack-test.component.scss
│ │ │ │ │ │ ├── stack-test.component.spec.ts
│ │ │ │ │ │ └── stack-test.component.ts
│ │ │ │ │ ├── stack.component.css
│ │ │ │ │ ├── stack.component.html
│ │ │ │ │ ├── stack.component.spec.ts
│ │ │ │ │ ├── stack.component.ts
│ │ │ │ │ └── stack.module.ts
│ │ │ │ ├── streaming/
│ │ │ │ │ ├── common.ts
│ │ │ │ │ ├── overlay/
│ │ │ │ │ │ ├── overlay.component.html
│ │ │ │ │ │ ├── overlay.component.scss
│ │ │ │ │ │ ├── overlay.component.spec.ts
│ │ │ │ │ │ └── overlay.component.ts
│ │ │ │ │ └── streaming.module.ts
│ │ │ │ ├── svg/
│ │ │ │ │ ├── samples/
│ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ ├── attr/
│ │ │ │ │ │ │ └── app.component.ts
│ │ │ │ │ │ ├── bs.module.ts
│ │ │ │ │ │ ├── chart/
│ │ │ │ │ │ │ └── app.component.ts
│ │ │ │ │ │ ├── chart2/
│ │ │ │ │ │ │ ├── app.component.solved.ts
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ └── ticks.component.ts
│ │ │ │ │ │ ├── chart3/
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ └── ticks.component.ts
│ │ │ │ │ │ ├── chart4/
│ │ │ │ │ │ │ ├── app.component.solved.ts
│ │ │ │ │ │ │ ├── app.component.ts
│ │ │ │ │ │ │ ├── app.module.ts
│ │ │ │ │ │ │ └── ticks.component.ts
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ ├── style.css
│ │ │ │ │ │ ├── sub.component.ts
│ │ │ │ │ │ └── svg/
│ │ │ │ │ │ └── app.component.ts
│ │ │ │ │ ├── svg-demo/
│ │ │ │ │ │ ├── svg-demo.component.css
│ │ │ │ │ │ ├── svg-demo.component.html
│ │ │ │ │ │ ├── svg-demo.component.spec.ts
│ │ │ │ │ │ └── svg-demo.component.ts
│ │ │ │ │ ├── svg-playground/
│ │ │ │ │ │ ├── svg-playground.component.css
│ │ │ │ │ │ ├── svg-playground.component.html
│ │ │ │ │ │ ├── svg-playground.component.spec.ts
│ │ │ │ │ │ └── svg-playground.component.ts
│ │ │ │ │ ├── svg-together/
│ │ │ │ │ │ ├── svg-together.component.css
│ │ │ │ │ │ ├── svg-together.component.html
│ │ │ │ │ │ ├── svg-together.component.spec.ts
│ │ │ │ │ │ └── svg-together.component.ts
│ │ │ │ │ ├── svg-together-result/
│ │ │ │ │ │ ├── svg-together-result.component.css
│ │ │ │ │ │ ├── svg-together-result.component.html
│ │ │ │ │ │ ├── svg-together-result.component.spec.ts
│ │ │ │ │ │ └── svg-together-result.component.ts
│ │ │ │ │ ├── svg.component.css
│ │ │ │ │ ├── svg.component.html
│ │ │ │ │ ├── svg.component.ts
│ │ │ │ │ ├── svg.module.ts
│ │ │ │ │ └── timer/
│ │ │ │ │ ├── timer.component.css
│ │ │ │ │ ├── timer.component.html
│ │ │ │ │ ├── timer.component.spec.ts
│ │ │ │ │ └── timer.component.ts
│ │ │ │ ├── svg-race/
│ │ │ │ │ ├── finish/
│ │ │ │ │ │ ├── finish.component.css
│ │ │ │ │ │ ├── finish.component.html
│ │ │ │ │ │ ├── finish.component.spec.ts
│ │ │ │ │ │ └── finish.component.ts
│ │ │ │ │ ├── little-car/
│ │ │ │ │ │ ├── little-car.component.css
│ │ │ │ │ │ ├── little-car.component.html
│ │ │ │ │ │ ├── little-car.component.spec.ts
│ │ │ │ │ │ └── little-car.component.ts
│ │ │ │ │ ├── player/
│ │ │ │ │ │ ├── player.component.css
│ │ │ │ │ │ ├── player.component.html
│ │ │ │ │ │ ├── player.component.spec.ts
│ │ │ │ │ │ └── player.component.ts
│ │ │ │ │ ├── race/
│ │ │ │ │ │ ├── race.component.css
│ │ │ │ │ │ ├── race.component.html
│ │ │ │ │ │ ├── race.component.spec.ts
│ │ │ │ │ │ └── race.component.ts
│ │ │ │ │ ├── svg-race.component.css
│ │ │ │ │ ├── svg-race.component.html
│ │ │ │ │ ├── svg-race.component.ts
│ │ │ │ │ ├── svg-race.module.ts
│ │ │ │ │ └── timer/
│ │ │ │ │ ├── timer.component.css
│ │ │ │ │ ├── timer.component.html
│ │ │ │ │ ├── timer.component.spec.ts
│ │ │ │ │ └── timer.component.ts
│ │ │ │ ├── sync/
│ │ │ │ │ ├── sync.component.css
│ │ │ │ │ ├── sync.component.html
│ │ │ │ │ ├── sync.component.spec.ts
│ │ │ │ │ ├── sync.component.ts
│ │ │ │ │ └── sync.module.ts
│ │ │ │ ├── test/
│ │ │ │ │ ├── test.component.css
│ │ │ │ │ ├── test.component.html
│ │ │ │ │ ├── test.component.spec.ts
│ │ │ │ │ ├── test.component.ts
│ │ │ │ │ └── test.module.ts
│ │ │ │ └── webassembly/
│ │ │ │ ├── ca/
│ │ │ │ │ ├── ca.module.ts
│ │ │ │ │ ├── single-cell/
│ │ │ │ │ │ ├── single-cell.component.css
│ │ │ │ │ │ ├── single-cell.component.html
│ │ │ │ │ │ ├── single-cell.component.spec.ts
│ │ │ │ │ │ └── single-cell.component.ts
│ │ │ │ │ └── single-grid/
│ │ │ │ │ ├── single-grid.component.css
│ │ │ │ │ ├── single-grid.component.html
│ │ │ │ │ ├── single-grid.component.spec.ts
│ │ │ │ │ └── single-grid.component.ts
│ │ │ │ ├── full-screen-runner/
│ │ │ │ │ ├── full-screen-runner.component.css
│ │ │ │ │ ├── full-screen-runner.component.html
│ │ │ │ │ ├── full-screen-runner.component.spec.ts
│ │ │ │ │ ├── full-screen-runner.component.ts
│ │ │ │ │ └── full-screen-runner.module.ts
│ │ │ │ ├── monaco-wat.ts
│ │ │ │ ├── samples/
│ │ │ │ │ ├── answer.wat
│ │ │ │ │ ├── base.js
│ │ │ │ │ ├── base.wat
│ │ │ │ │ └── old.wat
│ │ │ │ ├── tests/
│ │ │ │ │ ├── add-tests.ts
│ │ │ │ │ ├── common.ts
│ │ │ │ │ ├── disable-tests.ts
│ │ │ │ │ ├── enable-tests.ts
│ │ │ │ │ ├── evolve-cell.ts
│ │ │ │ │ ├── evolve-row.ts
│ │ │ │ │ ├── evolve.ts
│ │ │ │ │ ├── get-cell-score.ts
│ │ │ │ │ ├── get-index.ts
│ │ │ │ │ ├── load-cell.ts
│ │ │ │ │ ├── load-previous-cell.ts
│ │ │ │ │ ├── rotate.ts
│ │ │ │ │ ├── shift-tests.ts
│ │ │ │ │ └── store-cell-tests.ts
│ │ │ │ ├── utils.spec.ts
│ │ │ │ ├── utils.ts
│ │ │ │ ├── wasm-binary/
│ │ │ │ │ ├── test._wasm
│ │ │ │ │ ├── wasm-binary.component.css
│ │ │ │ │ ├── wasm-binary.component.html
│ │ │ │ │ ├── wasm-binary.component.spec.ts
│ │ │ │ │ ├── wasm-binary.component.ts
│ │ │ │ │ └── wasm-parser.ts
│ │ │ │ ├── webassembly-playground/
│ │ │ │ │ ├── error-message/
│ │ │ │ │ │ ├── error-message.component.css
│ │ │ │ │ │ ├── error-message.component.html
│ │ │ │ │ │ ├── error-message.component.spec.ts
│ │ │ │ │ │ └── error-message.component.ts
│ │ │ │ │ ├── monaco-directives/
│ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ ├── monaco-js-position.directive.ts
│ │ │ │ │ │ ├── monaco-load-answer.directive.ts
│ │ │ │ │ │ ├── monaco-scrolling.directive.ts
│ │ │ │ │ │ ├── monaco-wat-position.directive.spec.ts
│ │ │ │ │ │ └── monaco-wat-position.directive.ts
│ │ │ │ │ ├── runners/
│ │ │ │ │ │ └── wasm-test-runner/
│ │ │ │ │ │ ├── runner.js
│ │ │ │ │ │ ├── wasm-test-runner.component.html
│ │ │ │ │ │ ├── wasm-test-runner.component.scss
│ │ │ │ │ │ ├── wasm-test-runner.component.spec.ts
│ │ │ │ │ │ └── wasm-test-runner.component.ts
│ │ │ │ │ ├── viz/
│ │ │ │ │ │ ├── grid/
│ │ │ │ │ │ │ ├── grid.component.css
│ │ │ │ │ │ │ ├── grid.component.html
│ │ │ │ │ │ │ ├── grid.component.spec.ts
│ │ │ │ │ │ │ └── grid.component.ts
│ │ │ │ │ │ ├── viz.component.css
│ │ │ │ │ │ ├── viz.component.html
│ │ │ │ │ │ ├── viz.component.spec.ts
│ │ │ │ │ │ ├── viz.component.ts
│ │ │ │ │ │ └── viz.module.ts
│ │ │ │ │ ├── wasm-contents/
│ │ │ │ │ │ ├── wasm-contents.component.css
│ │ │ │ │ │ ├── wasm-contents.component.html
│ │ │ │ │ │ ├── wasm-contents.component.spec.ts
│ │ │ │ │ │ └── wasm-contents.component.ts
│ │ │ │ │ ├── web-assembly.service.spec.ts
│ │ │ │ │ ├── web-assembly.service.ts
│ │ │ │ │ ├── webassembly-code-mode/
│ │ │ │ │ │ ├── webassembly-code-mode.component.css
│ │ │ │ │ │ ├── webassembly-code-mode.component.html
│ │ │ │ │ │ ├── webassembly-code-mode.component.spec.ts
│ │ │ │ │ │ └── webassembly-code-mode.component.ts
│ │ │ │ │ ├── webassembly-playground.component.html
│ │ │ │ │ ├── webassembly-playground.component.scss
│ │ │ │ │ ├── webassembly-playground.component.spec.ts
│ │ │ │ │ ├── webassembly-playground.component.ts
│ │ │ │ │ └── webassembly-runner/
│ │ │ │ │ ├── webassembly-runner.component.css
│ │ │ │ │ ├── webassembly-runner.component.html
│ │ │ │ │ ├── webassembly-runner.component.spec.ts
│ │ │ │ │ ├── webassembly-runner.component.ts
│ │ │ │ │ └── webassembly-runner.module.ts
│ │ │ │ ├── webassembly.component.html
│ │ │ │ ├── webassembly.component.scss
│ │ │ │ ├── webassembly.component.spec.ts
│ │ │ │ ├── webassembly.component.ts
│ │ │ │ └── webassembly.module.ts
│ │ │ ├── assets/
│ │ │ │ ├── .gitkeep
│ │ │ │ └── runner/
│ │ │ │ └── index.html
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── locale/
│ │ │ │ ├── kirjs.ru.xtb
│ │ │ │ └── messages.xmb
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.css
│ │ │ └── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── lis/
│ │ ├── browserslist
│ │ ├── jest.config.js
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── app.component.css
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.spec.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ └── modules/
│ │ │ │ └── rxjs/
│ │ │ │ ├── rxjs.component.css
│ │ │ │ ├── rxjs.component.html
│ │ │ │ ├── rxjs.component.spec.ts
│ │ │ │ ├── rxjs.component.ts
│ │ │ │ └── rxjs.module.ts
│ │ │ ├── assets/
│ │ │ │ └── .gitkeep
│ │ │ ├── environments/
│ │ │ │ ├── environment.prod.ts
│ │ │ │ └── environment.ts
│ │ │ ├── index.html
│ │ │ ├── main.ts
│ │ │ ├── polyfills.ts
│ │ │ ├── styles.css
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ └── playground/
│ ├── browserslist
│ ├── jest.config.js
│ ├── src/
│ │ ├── app/
│ │ │ ├── app.component.html
│ │ │ ├── app.component.scss
│ │ │ ├── app.component.spec.ts
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── code-sync/
│ │ │ │ ├── code-sync.component.css
│ │ │ │ ├── code-sync.component.html
│ │ │ │ ├── code-sync.component.spec.ts
│ │ │ │ ├── code-sync.component.ts
│ │ │ │ └── code-sync.module.ts
│ │ │ └── playground/
│ │ │ ├── angular-sample.ts
│ │ │ ├── playground.component.css
│ │ │ ├── playground.component.html
│ │ │ ├── playground.component.spec.ts
│ │ │ ├── playground.component.ts
│ │ │ └── playground.module.ts
│ │ ├── assets/
│ │ │ └── .gitkeep
│ │ ├── environments/
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── index.html
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.scss
│ │ └── test-setup.ts
│ ├── tsconfig.app.json
│ ├── tsconfig.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── create-issue.js
├── cypress/
│ ├── fixtures/
│ │ └── example.json
│ ├── integration/
│ │ └── codelab/
│ │ └── home.spec.js
│ ├── plugins/
│ │ ├── cy-ts-preprocessor.js
│ │ └── index.js
│ ├── support/
│ │ ├── commands.js
│ │ └── index.js
│ └── tsconfig.json
├── cypress.json
├── docs/
│ ├── CONTRIBUTING.md
│ ├── HOSTING.md
│ └── TRANSLATING.md
├── firebase.json
├── jest.config.js
├── libs/
│ ├── angular-ast-viz/
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── ng-package.prod.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── angular-ast-viz.module.spec.ts
│ │ │ │ ├── angular-ast-viz.module.ts
│ │ │ │ ├── app.component.css
│ │ │ │ ├── app.component.html
│ │ │ │ ├── app.component.spec.ts
│ │ │ │ ├── app.component.ts
│ │ │ │ └── ast-tree/
│ │ │ │ ├── ast-tree.component.css
│ │ │ │ ├── ast-tree.component.html
│ │ │ │ ├── ast-tree.component.spec.ts
│ │ │ │ ├── ast-tree.component.ts
│ │ │ │ ├── ast-tree.module.ts
│ │ │ │ ├── short-name-babel.pipe.spec.ts
│ │ │ │ └── short-name-babel.pipe.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── angular-slides-to-pdf/
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── ng-package.prod.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── angular-slides-to-pdf.component.ts
│ │ │ │ ├── angular-slides-to-pdf.module.spec.ts
│ │ │ │ └── angular-slides-to-pdf.module.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── browser/
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── browser-window/
│ │ │ │ │ ├── browser-window.component.css
│ │ │ │ │ ├── browser-window.component.html
│ │ │ │ │ └── browser-window.component.ts
│ │ │ │ ├── browser.module.ts
│ │ │ │ ├── preview-window/
│ │ │ │ │ ├── preview-window.component.html
│ │ │ │ │ ├── preview-window.component.scss
│ │ │ │ │ └── preview-window.component.ts
│ │ │ │ └── terminal-window/
│ │ │ │ ├── terminal-window.component.css
│ │ │ │ ├── terminal-window.component.html
│ │ │ │ └── terminal-window.component.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── code-demos/
│ │ ├── assets/
│ │ │ └── runner/
│ │ │ ├── README.md
│ │ │ ├── index.html
│ │ │ ├── js/
│ │ │ │ ├── mocha.js
│ │ │ │ ├── system-config.js
│ │ │ │ └── test-bootstrap.js
│ │ │ ├── ng-dts/
│ │ │ │ ├── bundler.ts
│ │ │ │ └── files.txt
│ │ │ ├── ng2/
│ │ │ │ ├── basic.ts
│ │ │ │ ├── ng-bundle.js
│ │ │ │ ├── ng2-runner.js
│ │ │ │ └── rxjs.operators.ts
│ │ │ ├── package.json
│ │ │ └── tsconfig.json
│ │ ├── index.ts
│ │ ├── jest.config.js
│ │ ├── ng-package.json
│ │ ├── ng-package.prod.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── code-demo/
│ │ │ │ │ ├── code-demo.component.css
│ │ │ │ │ ├── code-demo.component.html
│ │ │ │ │ └── code-demo.component.ts
│ │ │ │ ├── code-demo-editor/
│ │ │ │ │ ├── code-demo-editor.component.ts
│ │ │ │ │ ├── code-demo-editor.injector.ts
│ │ │ │ │ ├── directives/
│ │ │ │ │ │ ├── code-demo-editor.auto-folding.directive.spec.ts
│ │ │ │ │ │ ├── code-demo-editor.auto-folding.directive.ts
│ │ │ │ │ │ ├── code-demo-editor.highlight.directive.ts
│ │ │ │ │ │ └── code-demo-editor.line-change.directive.ts
│ │ │ │ │ ├── editor.component.css
│ │ │ │ │ ├── themes/
│ │ │ │ │ │ └── devtools.json
│ │ │ │ │ └── utils/
│ │ │ │ │ ├── utils.spec.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ ├── code-demo-runner/
│ │ │ │ │ ├── code-demo-runner.component.css
│ │ │ │ │ ├── code-demo-runner.component.html
│ │ │ │ │ ├── code-demo-runner.component.spec.ts
│ │ │ │ │ └── code-demo-runner.component.ts
│ │ │ │ ├── code-demo.module.ts
│ │ │ │ ├── code-demos.module.spec.ts
│ │ │ │ ├── file-path/
│ │ │ │ │ ├── file-path.component.css
│ │ │ │ │ ├── file-path.component.html
│ │ │ │ │ ├── file-path.component.spec.ts
│ │ │ │ │ └── file-path.component.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── multitab-editor/
│ │ │ │ │ ├── editor-from-model/
│ │ │ │ │ │ ├── editor-from-model.component.css
│ │ │ │ │ │ ├── editor-from-model.component.html
│ │ │ │ │ │ ├── editor-from-model.component.spec.ts
│ │ │ │ │ │ └── editor-from-model.component.ts
│ │ │ │ │ ├── multitab-editor.component.css
│ │ │ │ │ ├── multitab-editor.component.html
│ │ │ │ │ ├── multitab-editor.component.spec.ts
│ │ │ │ │ └── multitab-editor.component.ts
│ │ │ │ ├── realtime-eval/
│ │ │ │ │ ├── realtime-eval.component.css
│ │ │ │ │ ├── realtime-eval.component.html
│ │ │ │ │ ├── realtime-eval.component.spec.ts
│ │ │ │ │ └── realtime-eval.component.ts
│ │ │ │ ├── runner/
│ │ │ │ │ ├── compile-ts-files.ts
│ │ │ │ │ └── prepare-templates.ts
│ │ │ │ └── shared/
│ │ │ │ ├── deps-order.service.spec.ts
│ │ │ │ ├── deps-order.service.ts
│ │ │ │ ├── helpers.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── loop-protection.service.spec.ts
│ │ │ │ ├── loop-protection.service.ts
│ │ │ │ ├── monaco-config.service.spec.ts
│ │ │ │ ├── monaco-config.service.ts
│ │ │ │ ├── monaco-replay.ts
│ │ │ │ ├── sandbox.ts
│ │ │ │ ├── script-loader.service.ts
│ │ │ │ ├── types-not-really.d.ts
│ │ │ │ ├── types.ts
│ │ │ │ ├── utils.ts
│ │ │ │ └── visitor.ts
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── console/
│ │ ├── karma.conf.js
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── console.component.css
│ │ │ │ ├── console.component.html
│ │ │ │ ├── console.component.spec.ts
│ │ │ │ ├── console.component.ts
│ │ │ │ ├── console.module.ts
│ │ │ │ └── display-dynamic.component/
│ │ │ │ ├── display-dynamic-component.component.css
│ │ │ │ ├── display-dynamic-component.component.html
│ │ │ │ ├── display-dynamic-component.component.spec.ts
│ │ │ │ └── display-dynamic-component.component.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── feedback/
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── ng-package.prod.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── feedback-issue-dropdown/
│ │ │ │ │ ├── feedback-issue-dropdown.component.html
│ │ │ │ │ ├── feedback-issue-dropdown.component.scss
│ │ │ │ │ └── feedback-issue-dropdown.component.ts
│ │ │ │ ├── feedback-rating/
│ │ │ │ │ ├── feedback-rating.component.css
│ │ │ │ │ ├── feedback-rating.component.html
│ │ │ │ │ └── feedback-rating.component.ts
│ │ │ │ ├── feedback-widget/
│ │ │ │ │ ├── feedback-widget.component.html
│ │ │ │ │ ├── feedback-widget.component.scss
│ │ │ │ │ └── feedback-widget.component.ts
│ │ │ │ ├── feedback.module.spec.ts
│ │ │ │ ├── feedback.module.ts
│ │ │ │ ├── feedback.service.spec.ts
│ │ │ │ ├── feedback.service.ts
│ │ │ │ └── message.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── firebase/
│ │ ├── README.md
│ │ ├── jest.config.js
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── firebase.module.spec.ts
│ │ │ │ ├── firebase.module.ts
│ │ │ │ ├── sync-fire-store.directive.spec.ts
│ │ │ │ └── sync-fire-store.directive.ts
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── firebase-login/
│ │ ├── index.ts
│ │ ├── karma.conf.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── firebase-login.module.spec.ts
│ │ │ │ ├── firebase-login.module.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── login-widget/
│ │ │ │ │ ├── login-widget.component.css
│ │ │ │ │ ├── login-widget.component.html
│ │ │ │ │ ├── login-widget.component.spec.ts
│ │ │ │ │ └── login-widget.component.ts
│ │ │ │ ├── login.service.spec.ts
│ │ │ │ └── login.service.ts
│ │ │ └── test.ts
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── intro/
│ │ ├── README.md
│ │ ├── generate/
│ │ │ ├── generate-thumbnails.ts
│ │ │ ├── helpers/
│ │ │ │ ├── const.ts
│ │ │ │ ├── slides.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── utils.ts
│ │ │ ├── slides.json
│ │ │ └── tsconfig.json
│ │ ├── jest.config.js
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── intro.component.css
│ │ │ │ ├── intro.component.html
│ │ │ │ ├── intro.component.ts
│ │ │ │ └── intro.module.ts
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.lib.prod.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── live/
│ │ ├── README.md
│ │ ├── jest.config.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ └── lib/
│ │ │ ├── live-service.service.spec.ts
│ │ │ └── live-service.service.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── slides/
│ │ ├── README.md
│ │ ├── jest.config.js
│ │ ├── ng-package.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── index.ts
│ │ │ ├── lib/
│ │ │ │ ├── arrows/
│ │ │ │ │ ├── slides-arrows.component.css
│ │ │ │ │ ├── slides-arrows.component.html
│ │ │ │ │ └── slides-arrows.component.ts
│ │ │ │ ├── deck/
│ │ │ │ │ ├── deck.component.html
│ │ │ │ │ ├── deck.component.scss
│ │ │ │ │ ├── deck.component.spec.ts
│ │ │ │ │ └── deck.component.ts
│ │ │ │ ├── full-screen-mode/
│ │ │ │ │ ├── full-screen-mode.service.spec.ts
│ │ │ │ │ ├── full-screen-mode.service.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── routing/
│ │ │ │ │ ├── slide-routes.ts
│ │ │ │ │ ├── slides-routing.directive.spec.ts
│ │ │ │ │ └── slides-routing.directive.ts
│ │ │ │ ├── shortcuts/
│ │ │ │ │ ├── shortcuts.directive.spec.ts
│ │ │ │ │ └── shortcuts.directive.ts
│ │ │ │ ├── slide/
│ │ │ │ │ ├── slide.directive.spec.ts
│ │ │ │ │ └── slide.directive.ts
│ │ │ │ ├── slides.module.spec.ts
│ │ │ │ └── slides.module.ts
│ │ │ └── test-setup.ts
│ │ ├── tsconfig.json
│ │ ├── tsconfig.lib.json
│ │ ├── tsconfig.lib.prod.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ └── utils/
│ ├── karma.conf.js
│ ├── src/
│ │ ├── index.ts
│ │ ├── lib/
│ │ │ ├── analytics/
│ │ │ │ ├── analytics.service.spec.ts
│ │ │ │ └── analytics.service.ts
│ │ │ ├── differ/
│ │ │ │ ├── diffFilesResolver.ts
│ │ │ │ ├── differ.spec.ts
│ │ │ │ ├── differ.ts
│ │ │ │ └── fileHelpers.ts
│ │ │ ├── github-PR-service/
│ │ │ │ ├── github.module.ts
│ │ │ │ ├── github.service.ts
│ │ │ │ └── index.ts
│ │ │ ├── i18n/
│ │ │ │ └── i18n-tools.ts
│ │ │ ├── index.ts
│ │ │ ├── loaders/
│ │ │ │ └── loaders.ts
│ │ │ ├── loading-indicator/
│ │ │ │ ├── loading-indicator/
│ │ │ │ │ ├── loading-indicator.component.css
│ │ │ │ │ ├── loading-indicator.component.html
│ │ │ │ │ ├── loading-indicator.component.spec.ts
│ │ │ │ │ └── loading-indicator.component.ts
│ │ │ │ └── loading-indicator.module.ts
│ │ │ ├── pipes/
│ │ │ │ ├── pipes.module.ts
│ │ │ │ └── safeHtml.pipe.ts
│ │ │ ├── sandbox-runner/
│ │ │ │ ├── common.spec.ts
│ │ │ │ ├── common.ts
│ │ │ │ ├── runners/
│ │ │ │ │ ├── runner.ts
│ │ │ │ │ ├── webworker.spec.ts
│ │ │ │ │ └── webworker.ts
│ │ │ │ ├── sandbox-runner.module.ts
│ │ │ │ ├── test-runner-service.service.spec.ts
│ │ │ │ ├── test-runner.component.html
│ │ │ │ ├── test-runner.component.scss
│ │ │ │ ├── test-runner.component.spec.ts
│ │ │ │ ├── test-runner.component.ts
│ │ │ │ ├── test-runner.service.ts
│ │ │ │ ├── test-runner.spec.ts
│ │ │ │ ├── test-runner.ts
│ │ │ │ ├── typescript-checker-runner/
│ │ │ │ │ ├── typescript-checker-runner.component.css
│ │ │ │ │ ├── typescript-checker-runner.component.html
│ │ │ │ │ ├── typescript-checker-runner.component.spec.ts
│ │ │ │ │ ├── typescript-checker-runner.component.ts
│ │ │ │ │ └── typescript-checker-runner.module.ts
│ │ │ │ └── typescript-test-runner/
│ │ │ │ ├── typescript-test-runner.component.css
│ │ │ │ ├── typescript-test-runner.component.html
│ │ │ │ ├── typescript-test-runner.component.spec.ts
│ │ │ │ └── typescript-test-runner.component.ts
│ │ │ ├── sync/
│ │ │ │ ├── common.ts
│ │ │ │ ├── components/
│ │ │ │ │ ├── configure-sync/
│ │ │ │ │ │ ├── configure-sync.component.css
│ │ │ │ │ │ ├── configure-sync.component.html
│ │ │ │ │ │ ├── configure-sync.component.spec.ts
│ │ │ │ │ │ ├── configure-sync.component.ts
│ │ │ │ │ │ └── configure-sync.module.ts
│ │ │ │ │ ├── online-indicator/
│ │ │ │ │ │ ├── online-indicator.component.css
│ │ │ │ │ │ ├── online-indicator.component.html
│ │ │ │ │ │ ├── online-indicator.component.spec.ts
│ │ │ │ │ │ ├── online-indicator.component.ts
│ │ │ │ │ │ └── online-indicator.module.ts
│ │ │ │ │ ├── poll/
│ │ │ │ │ │ ├── common/
│ │ │ │ │ │ │ ├── bar-chart/
│ │ │ │ │ │ │ │ ├── bar-chart.component.html
│ │ │ │ │ │ │ │ ├── bar-chart.component.scss
│ │ │ │ │ │ │ │ ├── bar-chart.component.spec.ts
│ │ │ │ │ │ │ │ ├── bar-chart.component.ts
│ │ │ │ │ │ │ │ └── bar-chart.module.ts
│ │ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ │ ├── stars/
│ │ │ │ │ │ │ │ ├── stars.component.css
│ │ │ │ │ │ │ │ ├── stars.component.html
│ │ │ │ │ │ │ │ ├── stars.component.spec.ts
│ │ │ │ │ │ │ │ ├── stars.component.ts
│ │ │ │ │ │ │ │ └── stars.module.ts
│ │ │ │ │ │ │ ├── sync-poll.service.spec.ts
│ │ │ │ │ │ │ └── sync-poll.service.ts
│ │ │ │ │ │ ├── sync-poll-admin/
│ │ │ │ │ │ │ ├── sync-poll-admin.component.css
│ │ │ │ │ │ │ ├── sync-poll-admin.component.html
│ │ │ │ │ │ │ ├── sync-poll-admin.component.spec.ts
│ │ │ │ │ │ │ └── sync-poll-admin.component.ts
│ │ │ │ │ │ ├── sync-poll-presenter/
│ │ │ │ │ │ │ ├── choice-presenter/
│ │ │ │ │ │ │ │ ├── choice-presenter.component.css
│ │ │ │ │ │ │ │ ├── choice-presenter.component.html
│ │ │ │ │ │ │ │ ├── choice-presenter.component.spec.ts
│ │ │ │ │ │ │ │ └── choice-presenter.component.ts
│ │ │ │ │ │ │ ├── leaderboard/
│ │ │ │ │ │ │ │ ├── leaderboard-presenter/
│ │ │ │ │ │ │ │ │ ├── leaderboard-presenter.component.css
│ │ │ │ │ │ │ │ │ ├── leaderboard-presenter.component.html
│ │ │ │ │ │ │ │ │ ├── leaderboard-presenter.component.spec.ts
│ │ │ │ │ │ │ │ │ └── leaderboard-presenter.component.ts
│ │ │ │ │ │ │ │ ├── leaderboard-viewer/
│ │ │ │ │ │ │ │ │ ├── leaderboard-viewer.component.css
│ │ │ │ │ │ │ │ │ ├── leaderboard-viewer.component.html
│ │ │ │ │ │ │ │ │ ├── leaderboard-viewer.component.spec.ts
│ │ │ │ │ │ │ │ │ └── leaderboard-viewer.component.ts
│ │ │ │ │ │ │ │ ├── leaderboard.component.css
│ │ │ │ │ │ │ │ ├── leaderboard.component.html
│ │ │ │ │ │ │ │ ├── leaderboard.component.spec.ts
│ │ │ │ │ │ │ │ ├── leaderboard.component.ts
│ │ │ │ │ │ │ │ └── leaderboard.module.ts
│ │ │ │ │ │ │ ├── stars-presenter/
│ │ │ │ │ │ │ │ ├── stars-presenter.component.css
│ │ │ │ │ │ │ │ ├── stars-presenter.component.html
│ │ │ │ │ │ │ │ ├── stars-presenter.component.spec.ts
│ │ │ │ │ │ │ │ └── stars-presenter.component.ts
│ │ │ │ │ │ │ ├── sync-poll-presenter.component.css
│ │ │ │ │ │ │ ├── sync-poll-presenter.component.html
│ │ │ │ │ │ │ ├── sync-poll-presenter.component.spec.ts
│ │ │ │ │ │ │ └── sync-poll-presenter.component.ts
│ │ │ │ │ │ ├── sync-poll-viewer/
│ │ │ │ │ │ │ ├── sync-poll-viewer-choice/
│ │ │ │ │ │ │ │ ├── sync-poll-viewer-choice.component.css
│ │ │ │ │ │ │ │ ├── sync-poll-viewer-choice.component.html
│ │ │ │ │ │ │ │ ├── sync-poll-viewer-choice.component.spec.ts
│ │ │ │ │ │ │ │ └── sync-poll-viewer-choice.component.ts
│ │ │ │ │ │ │ ├── sync-poll-viewer.component.css
│ │ │ │ │ │ │ ├── sync-poll-viewer.component.html
│ │ │ │ │ │ │ ├── sync-poll-viewer.component.spec.ts
│ │ │ │ │ │ │ └── sync-poll-viewer.component.ts
│ │ │ │ │ │ ├── sync-poll.component.css
│ │ │ │ │ │ ├── sync-poll.component.html
│ │ │ │ │ │ ├── sync-poll.component.spec.ts
│ │ │ │ │ │ ├── sync-poll.component.ts
│ │ │ │ │ │ └── sync-poll.module.ts
│ │ │ │ │ ├── questions/
│ │ │ │ │ │ ├── common/
│ │ │ │ │ │ │ ├── common.ts
│ │ │ │ │ │ │ ├── question/
│ │ │ │ │ │ │ │ ├── question.component.html
│ │ │ │ │ │ │ │ ├── question.component.scss
│ │ │ │ │ │ │ │ ├── question.component.spec.ts
│ │ │ │ │ │ │ │ └── question.component.ts
│ │ │ │ │ │ │ ├── question-list/
│ │ │ │ │ │ │ │ ├── question-list.component.css
│ │ │ │ │ │ │ │ ├── question-list.component.html
│ │ │ │ │ │ │ │ ├── question-list.component.spec.ts
│ │ │ │ │ │ │ │ └── question-list.component.ts
│ │ │ │ │ │ │ ├── questions.service.spec.ts
│ │ │ │ │ │ │ └── questions.service.ts
│ │ │ │ │ │ ├── questions-admin/
│ │ │ │ │ │ │ ├── questions-admin.component.css
│ │ │ │ │ │ │ ├── questions-admin.component.html
│ │ │ │ │ │ │ ├── questions-admin.component.spec.ts
│ │ │ │ │ │ │ └── questions-admin.component.ts
│ │ │ │ │ │ ├── questions-presenter/
│ │ │ │ │ │ │ ├── questions-presenter.component.css
│ │ │ │ │ │ │ ├── questions-presenter.component.html
│ │ │ │ │ │ │ ├── questions-presenter.component.spec.ts
│ │ │ │ │ │ │ └── questions-presenter.component.ts
│ │ │ │ │ │ ├── questions-viewer/
│ │ │ │ │ │ │ ├── questions-viewer.component.css
│ │ │ │ │ │ │ ├── questions-viewer.component.html
│ │ │ │ │ │ │ ├── questions-viewer.component.spec.ts
│ │ │ │ │ │ │ └── questions-viewer.component.ts
│ │ │ │ │ │ ├── questions.component.css
│ │ │ │ │ │ ├── questions.component.html
│ │ │ │ │ │ ├── questions.component.spec.ts
│ │ │ │ │ │ ├── questions.component.ts
│ │ │ │ │ │ └── questions.module.ts
│ │ │ │ │ ├── registration/
│ │ │ │ │ │ ├── registration-admin/
│ │ │ │ │ │ │ ├── registration-admin.component.css
│ │ │ │ │ │ │ ├── registration-admin.component.html
│ │ │ │ │ │ │ ├── registration-admin.component.spec.ts
│ │ │ │ │ │ │ └── registration-admin.component.ts
│ │ │ │ │ │ ├── registration-presenter/
│ │ │ │ │ │ │ ├── registration-presenter.component.css
│ │ │ │ │ │ │ ├── registration-presenter.component.html
│ │ │ │ │ │ │ ├── registration-presenter.component.spec.ts
│ │ │ │ │ │ │ └── registration-presenter.component.ts
│ │ │ │ │ │ ├── registration-viewer/
│ │ │ │ │ │ │ ├── registration-viewer.component.css
│ │ │ │ │ │ │ ├── registration-viewer.component.html
│ │ │ │ │ │ │ ├── registration-viewer.component.spec.ts
│ │ │ │ │ │ │ └── registration-viewer.component.ts
│ │ │ │ │ │ ├── registration.component.css
│ │ │ │ │ │ ├── registration.component.html
│ │ │ │ │ │ ├── registration.component.spec.ts
│ │ │ │ │ │ ├── registration.component.ts
│ │ │ │ │ │ ├── sync-registration.module.ts
│ │ │ │ │ │ ├── sync-registration.service.spec.ts
│ │ │ │ │ │ └── sync-registration.service.ts
│ │ │ │ │ ├── sync-code-game/
│ │ │ │ │ │ ├── sync-code-game-admin/
│ │ │ │ │ │ │ ├── sync-code-game-admin.component.css
│ │ │ │ │ │ │ ├── sync-code-game-admin.component.html
│ │ │ │ │ │ │ ├── sync-code-game-admin.component.spec.ts
│ │ │ │ │ │ │ └── sync-code-game-admin.component.ts
│ │ │ │ │ │ ├── sync-code-game-presenter/
│ │ │ │ │ │ │ ├── sync-code-game-presenter.component.css
│ │ │ │ │ │ │ ├── sync-code-game-presenter.component.html
│ │ │ │ │ │ │ ├── sync-code-game-presenter.component.spec.ts
│ │ │ │ │ │ │ └── sync-code-game-presenter.component.ts
│ │ │ │ │ │ ├── sync-code-game-viewer/
│ │ │ │ │ │ │ ├── sync-code-game-viewer.component.css
│ │ │ │ │ │ │ ├── sync-code-game-viewer.component.html
│ │ │ │ │ │ │ ├── sync-code-game-viewer.component.spec.ts
│ │ │ │ │ │ │ └── sync-code-game-viewer.component.ts
│ │ │ │ │ │ ├── sync-code-game.component.css
│ │ │ │ │ │ ├── sync-code-game.component.html
│ │ │ │ │ │ ├── sync-code-game.component.spec.ts
│ │ │ │ │ │ ├── sync-code-game.component.ts
│ │ │ │ │ │ ├── sync-code-game.module.ts
│ │ │ │ │ │ ├── sync-code-game.service.spec.ts
│ │ │ │ │ │ ├── sync-code-game.service.ts
│ │ │ │ │ │ └── tests.ts
│ │ │ │ │ ├── sync-join-instructions/
│ │ │ │ │ │ ├── sync-join-instructions.component.css
│ │ │ │ │ │ ├── sync-join-instructions.component.html
│ │ │ │ │ │ ├── sync-join-instructions.component.spec.ts
│ │ │ │ │ │ ├── sync-join-instructions.component.ts
│ │ │ │ │ │ └── sync-join-instructions.module.ts
│ │ │ │ │ └── sync-sessions/
│ │ │ │ │ ├── sync-sessions.component.css
│ │ │ │ │ ├── sync-sessions.component.html
│ │ │ │ │ ├── sync-sessions.component.spec.ts
│ │ │ │ │ ├── sync-sessions.component.ts
│ │ │ │ │ └── sync-sessions.module.ts
│ │ │ │ ├── directives/
│ │ │ │ │ ├── all-viewer-values.directive.ts
│ │ │ │ │ ├── is-status.directive.ts
│ │ │ │ │ ├── sync-directives.module.ts
│ │ │ │ │ ├── sync-is-presenting.directive.spec.ts
│ │ │ │ │ ├── sync-presenter-value.directive.ts
│ │ │ │ │ ├── sync-user-value.directive.ts
│ │ │ │ │ └── sync-viewer-value.directive.ts
│ │ │ │ ├── services/
│ │ │ │ │ ├── common.ts
│ │ │ │ │ ├── firebase-info.service.ts
│ │ │ │ │ ├── sync-data.service.ts
│ │ │ │ │ ├── sync-db-wrapper.service.spec.ts
│ │ │ │ │ ├── sync-db.service.ts
│ │ │ │ │ ├── sync-session.service.spec.ts
│ │ │ │ │ └── sync-session.service.ts
│ │ │ │ ├── sync-button/
│ │ │ │ │ ├── sync-button.component.css
│ │ │ │ │ ├── sync-button.component.html
│ │ │ │ │ ├── sync-button.component.spec.ts
│ │ │ │ │ ├── sync-button.component.ts
│ │ │ │ │ └── sync-button.module.ts
│ │ │ │ ├── sync-playground/
│ │ │ │ │ ├── sync-playground-presenter/
│ │ │ │ │ │ ├── sync-playground-presenter.component.css
│ │ │ │ │ │ ├── sync-playground-presenter.component.html
│ │ │ │ │ │ ├── sync-playground-presenter.component.spec.ts
│ │ │ │ │ │ └── sync-playground-presenter.component.ts
│ │ │ │ │ ├── sync-playground-test/
│ │ │ │ │ │ ├── sync-playground-test.component.css
│ │ │ │ │ │ ├── sync-playground-test.component.html
│ │ │ │ │ │ ├── sync-playground-test.component.spec.ts
│ │ │ │ │ │ └── sync-playground-test.component.ts
│ │ │ │ │ ├── sync-playground.component.html
│ │ │ │ │ ├── sync-playground.component.scss
│ │ │ │ │ ├── sync-playground.component.spec.ts
│ │ │ │ │ └── sync-playground.component.ts
│ │ │ │ └── sync.module.ts
│ │ │ ├── test-results/
│ │ │ │ ├── common.ts
│ │ │ │ ├── file-aware-description/
│ │ │ │ │ ├── file-aware-description.component.css
│ │ │ │ │ ├── file-aware-description.component.html
│ │ │ │ │ ├── file-aware-description.component.spec.ts
│ │ │ │ │ └── file-aware-description.component.ts
│ │ │ │ ├── simple-tests-progress/
│ │ │ │ │ ├── simple-tests-progress.component.css
│ │ │ │ │ ├── simple-tests-progress.component.html
│ │ │ │ │ ├── simple-tests-progress.component.spec.ts
│ │ │ │ │ ├── simple-tests-progress.component.ts
│ │ │ │ │ └── simple-tests-progress.module.ts
│ │ │ │ ├── test-results/
│ │ │ │ │ ├── test-results.component.html
│ │ │ │ │ ├── test-results.component.scss
│ │ │ │ │ ├── test-results.component.spec.ts
│ │ │ │ │ └── test-results.component.ts
│ │ │ │ ├── test-results.module.ts
│ │ │ │ └── test-run-results/
│ │ │ │ ├── test-run-results.component.html
│ │ │ │ ├── test-run-results.component.scss
│ │ │ │ ├── test-run-results.component.spec.ts
│ │ │ │ └── test-run-results.component.ts
│ │ │ ├── testing/
│ │ │ │ └── mocks/
│ │ │ │ ├── angular-fire.ts
│ │ │ │ └── sync-db-service.ts
│ │ │ └── tracking/
│ │ │ ├── tracking.directive.ts
│ │ │ └── tracking.module.ts
│ │ └── test.ts
│ ├── tsconfig.json
│ ├── tsconfig.lib.json
│ ├── tsconfig.spec.json
│ └── tslint.json
├── ng2ts/
│ ├── .prettierignore
│ ├── api.service.ts
│ ├── app.component.ts
│ ├── app.html
│ ├── app.module.ts
│ ├── code.ts
│ ├── context/
│ │ ├── context.component.ts
│ │ ├── context.html
│ │ └── context.service.ts
│ ├── data-binding/
│ │ ├── DataBinding.ts
│ │ └── DataBindingModule.ts
│ ├── fuzzy-pipe/
│ │ └── fuzzy.pipe.ts
│ ├── main.ts
│ ├── ng2ts.ts
│ ├── search/
│ │ ├── search.component.html
│ │ └── search.component.ts
│ ├── style.css
│ ├── tests/
│ │ ├── ThumbsComponentCreateTest.ts
│ │ ├── ThumbsComponentUseTest.ts
│ │ ├── bootstrapTest.ts
│ │ ├── codelabTest.ts
│ │ ├── contextComponentUseTest.ts
│ │ ├── createComponentTest.ts
│ │ ├── createModuleTest.ts
│ │ ├── diInjectServiceTest.ts
│ │ ├── diInjectServiceTestBabel.ts
│ │ ├── formsTest.ts
│ │ ├── fuzzyPipeCreateTest.ts
│ │ ├── fuzzyPipeUseTest.ts
│ │ ├── materialTest.ts
│ │ ├── routerTest.ts
│ │ ├── templateAddActionTest.ts
│ │ ├── templateAllVideosTest.ts
│ │ ├── templatePageSetupTest.ts
│ │ ├── test.ts
│ │ ├── togglePanelComponentCreateTest.ts
│ │ ├── togglePanelComponentUseTest.ts
│ │ ├── videoComponentCreateTest.ts
│ │ └── videoComponentUseTest.ts
│ ├── thumbs/
│ │ ├── thumbs.component.ts
│ │ └── thumbs.html
│ ├── thumbs.app.module.ts
│ ├── toggle-panel/
│ │ ├── toggle-panel.component.ts
│ │ └── toggle-panel.html
│ ├── toggle-panel.app.module.ts
│ ├── typescript-intro/
│ │ ├── Codelab.ts
│ │ ├── Guest.ts
│ │ └── Main.ts
│ ├── upload/
│ │ ├── upload.component.html
│ │ └── upload.component.ts
│ ├── video/
│ │ ├── video-item.ts
│ │ ├── video-materialized.component.html
│ │ ├── video-wrapper.component.ts
│ │ ├── video.component.html
│ │ ├── video.component.ts
│ │ ├── video.index.html
│ │ └── video.service.ts
│ ├── video.app.module.ts
│ └── wrapper.component.ts
├── nx.json
├── package.json
├── tools/
│ ├── schematics/
│ │ └── slide/
│ │ ├── README.md
│ │ ├── files/
│ │ │ ├── code.bs
│ │ │ └── template.component.html
│ │ ├── index.ts
│ │ └── schema.json
│ └── tsconfig.tools.json
├── tsconfig.json
└── tslint.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# Editor configuration, see http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false
================================================
FILE: .firebaserc
================================================
{
"projects": {
"default": "angular-presentation",
"kirjs": "kirjs-kirjs"
},
"targets": {
"angular-presentation": {
"hosting": {
"codelab": [
"angular-presentation"
],
"kirjs": [
"kirjs-home"
],
"codelab-next": [
"codelab-next"
],
"lis": [
"lis-lis"
],
"angular-ivy": [
"angular-ivy"
]
}
}
}
}
================================================
FILE: .flooignore
================================================
extern
node_modules
tmp
vendor
.idea/workspace.xml
.idea/misc.xml
================================================
FILE: .gitattributes
================================================
src/assets/monaco linguist-vendored
================================================
FILE: .gitignore
================================================
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
yarn.lock
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/typings
.firebase
*.log
.floo
# e2e
/e2e/*.js
/e2e/*.map
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
================================================
FILE: .nvmrc
================================================
10.17.0
================================================
FILE: .prettierignore
================================================
ng2ts
dist
coverage
libs/code-demos/assets/runner/ng2/ng-bundle.js
libs/code-demos/assets/runner/ng-dts/files.txt
================================================
FILE: .prettierrc
================================================
{
"singleQuote": true
}
================================================
FILE: .travis.yml
================================================
dist: trusty
language: node_js
node_js:
- "10"
env:
matrix:
- TRIGGER="format:check"
- TRIGGER="lint"
- TRIGGER="ng build -- --prod --source-map=false --build-optimizer=false"
script:
- npm run $TRIGGER
notifications:
email: false
cache:
npm: true
directories:
- node_modules
deploy:
provider: firebase
project: "angular-presentation"
token:
secure: "ed9U/81s4sR9ihLEogOdbXlKin0/vhJdk9XaLLTv6jVxGG/n40tcwwsCze6F3sBTibjq90OJHZn1ip+4uDdF+BzdzJ+hjU0SCR4B/Fi3OtxW73vz//ou1BktVrkHh+kzyS+3AvLQ9xvkz1h57vfzgfW8V/Jhy3PviZXvoPO4ttOQs2HwbO3C0BvwIaneRlNDudRz/rIQDHxHPaR6Hh3gG69vBc6D0eWhbMbFcWr6kHN3t2ki3APFR3omtNhZIdrFF9icSkeQ1fwiMKKfQaxmHXT1HZhxSCfvcqYwIqqidcFWTpABlt+2MNpWjnPLJV0l4D2HGSVF4RWtLFqfHhm/cyJTB/1ejDB92U4bA5W/tSGGgry5mgvD2pGrRHqHt+awtP9G4hgrZB3oADj3Q0nbj4VRDMgAYbAvrRYSFzxeXg50j7NwogGh02osAGpBhZzJA1I93c2yEPmcu+ysG1FkCnggh65LYX3Zjyu2eRyuPj0kaT+5OhFUanpCYO2cM/pGFMnG8+InvwLKUKfAtgUrP6yefd5CGJZaWbLGemNue6teAyMVWye4Ep2swY3DW0di5Ikn5lrV2V33yzeNd2OoSQbCaBbFLEYrlHw1+/7yNf928jj4vG0TYJwdUhlZDWdkogFIGx5cyu7dNEa5UnlzhJFkqIoo77kQrD0PKCyoAVg="
before_deploy:
- npm run pre-deploy
on:
branch: release
================================================
FILE: .vscode/extensions.json
================================================
{
"recommendations": [
"nrwl.angular-console",
"angular.ng-template",
"esbenp.prettier-vscode"
]
}
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 Kirill Cherkashin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
[](https://nycjsorg.now.sh) [](https://codelab.fun)
# Angular Codelab - [codelab.fun](https://codelab.fun)
Angular Codelab is a self-paced interactive Angular course:
- 🔥 Everything in the browser - **no setup needed**
- 🔥 **Interactive code samples** - Change the code and see the result immediately!
- 🔥 Hands-on **Exercises** to solidify your knowledge
- 🔥 **Free** - Made for Angular enthusiasts by Angular enthusiasts
- 🔥 Written in Angular and **open source**
Try it out yourself at [codelab.fun](https://codelab.fun)
## Help us make Codelab.fun better
- [Contribute to the codebase](#contribute-to-the-codebase)
- [Host a live Angular Codelab event in your city](#host-a-live-angular-codelab-event-in-your-city)
- [Translate Codelab to your language](#translate-codelab-to-your-language)
### Contribute to the codebase
Codelab.fun is an Angular app!
It uses [angular-cli](https://cli.angular.io/) with [nx](https://nx.dev) and all materials are in a form of Angular components.

See what [Good first issues](https://github.com/codelab-fun/codelab/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue) are available.
See [CONTRIBUTING.md](./docs/CONTRIBUTING.md) for detailed instructions.
### Host a live Angular Codelab event in your city
We have hosted multiple live codelab events in 10+ cities, and you can help us host one in yours!
See how in [HOSTING.md](./docs/HOSTING.md)
### Translate Codelab to your language
Currently codelab.fun is availble in English and Russian.
See [TRANSLATING.md](./docs/TRANSLATING.md) for how you can help us translate the codelab into your language.
Thanks to amazing [PoEditor](https://poeditor.com) for providing us with an open source licence
================================================
FILE: angular.json
================================================
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "",
"projects": {
"codelab": {
"root": "apps/codelab",
"sourceRoot": "apps/codelab/src",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "apps/codelab/extra-webpack.config.js"
},
"outputPath": "dist/apps/codelab",
"index": "apps/codelab/src/index.html",
"main": "apps/codelab/src/main.ts",
"tsConfig": "apps/codelab/tsconfig.app.json",
"polyfills": "apps/codelab/src/polyfills.ts",
"aot": true,
"assets": [
"apps/codelab/src/assets",
"apps/codelab/src/favicon.ico",
"apps/codelab/src/manifest.webmanifest",
{
"glob": "**/*",
"input": "node_modules/monaco-editor/",
"output": "./assets/monaco/"
},
{
"glob": "**/*",
"input": "libs/intro/assets/",
"output": "./assets/intro/"
},
{
"glob": "**/*",
"input": "libs/code-demos/assets/runner/",
"output": "./assets/runner/"
}
],
"styles": ["apps/codelab/src/styles.scss"],
"scripts": []
},
"configurations": {
"ru": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": true,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"baseHref": "/ru/",
"deployUrl": "/ru/",
"fileReplacements": [
{
"replace": "apps/codelab/src/environments/environment.ts",
"with": "apps/codelab/src/environments/environment.prod.ts"
}
],
"outputPath": "dist/apps/codelab/ru",
"i18nFile": "apps/codelab/src/locale/codelab.ru.xtb",
"i18nFormat": "xtb",
"i18nLocale": "ru"
},
"production": {
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "apps/codelab/src/environments/environment.ts",
"with": "apps/codelab/src/environments/environment.prod.ts"
}
],
"serviceWorker": false
}
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "codelab:build"
},
"configurations": {
"production": {
"browserTarget": "codelab:build:production"
},
"ru": {
"browserTarget": "codelab:build:ru"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "codelab:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/codelab/src/test.ts",
"karmaConfig": "apps/codelab/karma.conf.js",
"polyfills": "apps/codelab/src/polyfills.ts",
"tsConfig": "apps/codelab/tsconfig.spec.json",
"scripts": [],
"styles": ["apps/codelab/src/styles.scss"],
"assets": [
"apps/codelab/src/assets",
"apps/codelab/src/favicon.ico",
"apps/codelab/src/manifest.webmanifest"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/codelab/tsconfig.app.json",
"apps/codelab/tsconfig.spec.json"
],
"exclude": []
}
}
}
},
"browser": {
"root": "libs/browser",
"sourceRoot": "libs/browser/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/browser/tsconfig.lib.json",
"libs/browser/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"console": {
"root": "libs/console",
"sourceRoot": "libs/console/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/console/src/test.ts",
"tsConfig": "libs/console/tsconfig.spec.json",
"karmaConfig": "libs/console/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/console/tsconfig.lib.json",
"libs/console/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"utils": {
"root": "libs/utils",
"sourceRoot": "libs/utils/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/utils/src/test.ts",
"tsConfig": "libs/utils/tsconfig.spec.json",
"karmaConfig": "libs/utils/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/utils/tsconfig.lib.json",
"libs/utils/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"kirjs": {
"root": "apps/kirjs/",
"sourceRoot": "apps/kirjs/src",
"projectType": "application",
"prefix": "kirjs",
"schematics": {},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"aot": true,
"outputPath": "dist/apps/kirjs",
"index": "apps/kirjs/src/index.html",
"main": "apps/kirjs/src/main.ts",
"polyfills": "apps/kirjs/src/polyfills.ts",
"tsConfig": "apps/kirjs/tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "node_modules/monaco-editor/",
"output": "./assets/monaco/"
},
"apps/kirjs/src/favicon.ico",
"apps/kirjs/src/assets",
{
"glob": "**/*",
"input": "libs/code-demos/assets/runner/",
"output": "./assets/runner/"
}
],
"styles": ["apps/kirjs/src/styles.css"],
"scripts": []
},
"configurations": {
"ru": {
"outputPath": "dist/apps/kirjs/ru",
"aot": true,
"i18nFile": "apps/kirjs/src/locale/kirjs.ru.xtb",
"i18nFormat": "xtb",
"i18nLocale": "ru"
},
"production": {
"fileReplacements": [
{
"replace": "apps/kirjs/src/environments/environment.ts",
"with": "apps/kirjs/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "kirjs:build"
},
"configurations": {
"production": {
"browserTarget": "kirjs:build:production"
},
"ru": {
"browserTarget": "kirjs:build:ru"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "kirjs:build"
},
"configurations": {
"ru": {
"outputPath": "locale/",
"outFile": "messages.ru.untranslated.xlf",
"i18nFormat": "xlf",
"i18nLocale": "ru"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/kirjs/src/test.ts",
"polyfills": "apps/kirjs/src/polyfills.ts",
"tsConfig": "apps/kirjs/tsconfig.spec.json",
"karmaConfig": "apps/kirjs/karma.conf.js",
"styles": ["apps/kirjs/src/styles.css"],
"scripts": [],
"assets": ["apps/kirjs/src/favicon.ico", "apps/kirjs/src/assets"]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/kirjs/tsconfig.app.json",
"apps/kirjs/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"angular-ast-viz": {
"root": "libs/angular-ast-viz",
"sourceRoot": "libs/angular-ast-viz/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/angular-ast-viz/tsconfig.lib.json",
"project": "libs/angular-ast-viz/ng-package.json"
},
"configurations": {
"production": {
"project": "libs/angular-ast-viz/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/angular-ast-viz/src/test.ts",
"tsConfig": "libs/angular-ast-viz/tsconfig.spec.json",
"karmaConfig": "libs/angular-ast-viz/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/angular-ast-viz/tsconfig.lib.json",
"libs/angular-ast-viz/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"angular-slides-to-pdf": {
"root": "libs/angular-slides-to-pdf",
"sourceRoot": "libs/angular-slides-to-pdf/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/angular-slides-to-pdf/tsconfig.lib.json",
"project": "libs/angular-slides-to-pdf/ng-package.json"
},
"configurations": {
"production": {
"project": "libs/angular-slides-to-pdf/ng-package.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/angular-slides-to-pdf/src/test.ts",
"tsConfig": "libs/angular-slides-to-pdf/tsconfig.spec.json",
"karmaConfig": "libs/angular-slides-to-pdf/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/angular-slides-to-pdf/tsconfig.lib.json",
"libs/angular-slides-to-pdf/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"feedback": {
"root": "libs/feedback",
"sourceRoot": "libs/feedback/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/feedback/tsconfig.lib.json",
"project": "libs/feedback/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/feedback/src/test.ts",
"tsConfig": "libs/feedback/tsconfig.spec.json",
"karmaConfig": "libs/feedback/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/feedback/tsconfig.lib.json",
"libs/feedback/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"code-demos": {
"root": "libs/code-demos",
"sourceRoot": "libs/code-demos/src",
"projectType": "library",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/code-demos/tsconfig.lib.json",
"project": "libs/code-demos/ng-package.json"
},
"configurations": {
"production": {
"project": "libs/code-demos/ng-package.prod.json"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/code-demos/tsconfig.lib.json",
"libs/code-demos/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/code-demos/jest.config.js",
"tsConfig": "libs/code-demos/tsconfig.spec.json",
"setupFile": "libs/code-demos/src/test-setup.ts"
}
}
}
},
"live": {
"root": "libs/live",
"sourceRoot": "libs/live/src",
"projectType": "library",
"prefix": "live",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/live/tsconfig.lib.json",
"project": "libs/live/ng-package.json"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/live/tsconfig.lib.json",
"libs/live/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/live/jest.config.js",
"tsConfig": "libs/live/tsconfig.spec.json"
}
}
},
"schematics": {}
},
"firebase-login": {
"root": "libs/firebase-login",
"sourceRoot": "libs/firebase-login/src",
"projectType": "library",
"prefix": "angular-presentation",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/firebase-login/tsconfig.lib.json",
"project": "libs/firebase-login/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/firebase-login/src/test.ts",
"tsConfig": "libs/firebase-login/tsconfig.spec.json",
"karmaConfig": "libs/firebase-login/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/firebase-login/tsconfig.lib.json",
"libs/firebase-login/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"blog": {
"root": "apps/blog/",
"sourceRoot": "apps/blog/src",
"projectType": "application",
"prefix": "codelab",
"schematics": {
"@nrwl/schematics:component": {
"style": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/blog",
"index": "apps/blog/src/index.html",
"main": "apps/blog/src/main.ts",
"polyfills": "apps/blog/src/polyfills.ts",
"tsConfig": "apps/blog/tsconfig.app.json",
"assets": ["apps/blog/src/favicon.ico", "apps/blog/src/assets"],
"styles": ["apps/blog/src/styles.scss"],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/blog/src/environments/environment.ts",
"with": "apps/blog/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "20mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "blog:build"
},
"configurations": {
"production": {
"browserTarget": "blog:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "blog:build"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/blog/tsconfig.app.json",
"apps/blog/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/blog/jest.config.js",
"tsConfig": "apps/blog/tsconfig.spec.json",
"setupFile": "apps/blog/src/test-setup.ts"
}
}
}
},
"angular-thirty-seconds": {
"root": "apps/angular-thirty-seconds/",
"sourceRoot": "apps/angular-thirty-seconds/src",
"projectType": "application",
"prefix": "codelab",
"schematics": {
"@nrwl/schematics:component": {
"style": "scss"
}
},
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/codelab/30",
"index": "apps/angular-thirty-seconds/src/index.html",
"main": "apps/angular-thirty-seconds/src/main.ts",
"polyfills": "apps/angular-thirty-seconds/src/polyfills.ts",
"tsConfig": "apps/angular-thirty-seconds/tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "node_modules/monaco-editor/",
"output": "./assets/monaco/"
},
"apps/angular-thirty-seconds/src/favicon.ico",
"apps/angular-thirty-seconds/src/assets",
{
"glob": "**/*",
"input": "libs/code-demos/assets/runner/",
"output": "./assets/runner/"
}
],
"styles": [
"apps/angular-thirty-seconds/src/styles.scss",
"node_modules/prismjs/themes/prism-okaidia.css",
"node_modules/prismjs/plugins/line-numbers/prism-line-numbers.css"
],
"scripts": [
"node_modules/prismjs/prism.js",
"node_modules/prismjs/components/prism-typescript.min.js",
"node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/angular-thirty-seconds/src/environments/environment.ts",
"with": "apps/angular-thirty-seconds/src/environments/environment.prod.ts"
}
],
"baseHref": "/30/",
"deployUrl": "/30/",
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "60mb",
"maximumError": "70mb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "angular-thirty-seconds:build"
},
"configurations": {
"production": {
"browserTarget": "angular-thirty-seconds:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "angular-thirty-seconds:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/angular-thirty-seconds/src/test.ts",
"polyfills": "apps/angular-thirty-seconds/src/polyfills.ts",
"tsConfig": "apps/angular-thirty-seconds/tsconfig.spec.json",
"karmaConfig": "apps/angular-thirty-seconds/karma.conf.js",
"styles": ["apps/angular-thirty-seconds/src/styles.scss"],
"scripts": [],
"assets": [
"apps/angular-thirty-seconds/src/favicon.ico",
"apps/angular-thirty-seconds/src/assets"
]
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/angular-thirty-seconds/tsconfig.app.json",
"apps/angular-thirty-seconds/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"lis": {
"projectType": "application",
"schematics": {},
"root": "apps/lis",
"sourceRoot": "apps/lis/src",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/lis",
"index": "apps/lis/src/index.html",
"main": "apps/lis/src/main.ts",
"polyfills": "apps/lis/src/polyfills.ts",
"tsConfig": "apps/lis/tsconfig.app.json",
"aot": false,
"assets": [
"apps/lis/src/favicon.ico",
"apps/lis/src/assets",
{
"glob": "**/*",
"input": "node_modules/monaco-editor/",
"output": "./assets/monaco/"
},
{
"glob": "**/*",
"input": "libs/code-demos/assets/runner/",
"output": "./assets/runner/"
}
],
"styles": ["apps/lis/src/styles.css"],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/lis/src/environments/environment.ts",
"with": "apps/lis/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "lis:build"
},
"configurations": {
"production": {
"browserTarget": "lis:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "lis:build"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/lis/tsconfig.app.json",
"apps/lis/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!apps/lis/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/lis/jest.config.js",
"tsConfig": "apps/lis/tsconfig.spec.json",
"setupFile": "apps/lis/src/test-setup.ts"
}
}
}
},
"playground": {
"projectType": "application",
"schematics": {
"@nrwl/angular:component": {
"style": "scss"
}
},
"root": "apps/playground",
"sourceRoot": "apps/playground/src",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/apps/playground",
"index": "apps/playground/src/index.html",
"main": "apps/playground/src/main.ts",
"polyfills": "apps/playground/src/polyfills.ts",
"tsConfig": "apps/playground/tsconfig.app.json",
"aot": true,
"assets": [
"apps/playground/src/favicon.ico",
"apps/playground/src/assets",
{
"glob": "**/*",
"input": "node_modules/monaco-editor/",
"output": "./assets/monaco/"
}
],
"styles": ["apps/playground/src/styles.scss"],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/playground/src/environments/environment.ts",
"with": "apps/playground/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "20mb",
"maximumError": "25mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "playground:build"
},
"configurations": {
"production": {
"browserTarget": "playground:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "playground:build"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"apps/playground/tsconfig.app.json",
"apps/playground/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!apps/playground/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/playground/jest.config.js",
"tsConfig": "apps/playground/tsconfig.spec.json",
"setupFile": "apps/playground/src/test-setup.ts"
}
}
}
},
"firebase": {
"projectType": "library",
"root": "libs/firebase",
"sourceRoot": "libs/firebase/src",
"prefix": "codelab",
"architect": {
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/firebase/tsconfig.lib.json",
"libs/firebase/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/firebase/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/firebase/jest.config.js",
"tsConfig": "libs/firebase/tsconfig.spec.json",
"setupFile": "libs/firebase/src/test-setup.ts"
}
}
},
"schematics": {}
},
"intro": {
"projectType": "library",
"root": "libs/intro",
"sourceRoot": "libs/intro/src",
"prefix": "codelab",
"architect": {
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/intro/tsconfig.lib.json",
"libs/intro/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/intro/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/intro/jest.config.js",
"tsConfig": "libs/intro/tsconfig.spec.json",
"setupFile": "libs/intro/src/test-setup.ts"
}
}
},
"schematics": {}
},
"slides": {
"projectType": "library",
"root": "libs/slides",
"sourceRoot": "libs/slides/src",
"prefix": "codelab",
"architect": {
"build": {
"builder": "@nrwl/angular:package",
"options": {
"tsConfig": "libs/slides/tsconfig.lib.json",
"project": "libs/slides/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "libs/slides/tsconfig.lib.prod.json"
}
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"libs/slides/tsconfig.lib.json",
"libs/slides/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/slides/**"]
}
},
"test": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/slides/jest.config.js",
"tsConfig": "libs/slides/tsconfig.spec.json",
"setupFile": "libs/slides/src/test-setup.ts"
}
}
},
"schematics": {}
}
},
"defaultProject": "codelab",
"schematics": {
"@schematics/angular:component": {
"prefix": "slides",
"style": "css"
},
"@schematics/angular:directive": {
"prefix": "slides"
},
"@nrwl/schematics:library": {
"unitTestRunner": "karma",
"framework": "angular"
},
"@nrwl/schematics:application": {
"unitTestRunner": "karma",
"e2eTestRunner": "protractor"
},
"@nrwl/schematics:node-application": {
"framework": "express"
},
"@nrwl/angular:application": {
"unitTestRunner": "jest",
"e2eTestRunner": "cypress"
},
"@nrwl/angular:library": {
"unitTestRunner": "jest"
}
},
"cli": {
"defaultCollection": "@nrwl/angular",
"analytics": "2d33a6dc-15e8-4227-b14e-dedd96805cec"
}
}
================================================
FILE: apps/angular-thirty-seconds/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11
================================================
FILE: apps/angular-thirty-seconds/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '../',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: apps/angular-thirty-seconds/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-root',
template: `
`,
styles: [
`
.wrapper {
margin: 0 60px;
padding: 0 20px;
}
:host ::ng-deep {
font-family: 'Helvetica Neue', sans-serif;
font-weight: 300;
padding: 0 20px;
display: block;
}
`
]
})
export class AppComponent {}
================================================
FILE: apps/angular-thirty-seconds/src/app/app.module.ts
================================================
import { MatButtonModule } from '@angular/material/button';
import { MatTableModule } from '@angular/material/table';
import { BrowserModule } from '@angular/platform-browser';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { RouterModule, Routes } from '@angular/router';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { monacoReady } from '@codelab/code-demos';
import { HttpClientModule } from '@angular/common/http';
import { CreateSnippetComponent } from './create-snippet/create-snippet.component';
import { CreateSnippetModule } from './create-snippet/create-snippet.module';
import { environment } from '../../../codelab/src/environments/environment';
import { AngularFireModule } from '@angular/fire';
import { PullRequestsListComponent } from './pull-requests-list/pull-requests-list.component';
export const angularFire = AngularFireModule.initializeApp(
environment.firebaseConfig
);
const routes: Routes = [
{ path: '', redirectTo: 'list', pathMatch: 'full' },
{ path: 'list', component: PullRequestsListComponent },
{ path: 'new/:repoName/:repoOwner', component: CreateSnippetComponent },
{
path: 'new/:repoName/:repoOwner/:pullNumber',
component: CreateSnippetComponent
}
];
@NgModule({
imports: [
angularFire,
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot(routes),
MatButtonModule,
MatTableModule,
HttpClientModule,
CreateSnippetModule
],
declarations: [AppComponent, PullRequestsListComponent],
providers: [
{
provide: APP_INITIALIZER,
useValue: monacoReady,
multi: true
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/create-snippet.component.html
================================================
{{ isEditing ? '' : 'New' }} Snippet
{{ isEditing ? 'Edit' : 'Create' }} Snippet
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/create-snippet.component.scss
================================================
:host {
label {
color: #3e515b;
}
textarea {
width: 100%;
height: 280px;
}
mat-form-field {
width: 100%;
}
section {
display: flex;
margin: 30px 0;
}
.icon {
justify-content: center;
align-items: center;
font-size: 15px;
&-plus:before {
content: '\FF0B';
border: 1px solid #444;
border-radius: 50%;
color: #444;
}
&-minus:before {
content: '\FF0D';
border: 1px solid #444;
border-radius: 50%;
color: #444;
}
}
.btn-submit {
float: right;
font-weight: 400;
text-align: center;
vertical-align: middle;
border: 1px solid #213451;
font-size: 14px;
color: #fff;
background-color: #213451;
cursor: pointer;
}
.container {
max-width: 850px;
padding: 0 15px 30px 15px;
margin: 0 auto;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
}
.input-info {
display: block;
height: 10px;
line-height: 10px;
font-size: 0.625em;
margin-top: -15px;
color: #8b8b8b;
}
.markdown-info {
width: 50%;
padding: 15px 35px;
border: 1px solid #a4b7c1;
margin-left: 30px;
}
.input-block {
width: 300px;
div {
margin-bottom: 25px;
width: 100%;
}
}
.required {
color: red;
}
.cursor-pointer {
cursor: pointer;
}
.w {
&-100 {
width: 100%;
}
&-50 {
width: 50%;
}
}
.validation-message {
color: red;
position: absolute;
width: 100%;
height: 1em;
margin-top: 5px;
line-height: 1em;
font-size: 0.75em;
text-align: left;
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/create-snippet.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateSnippetComponent } from './create-snippet.component';
import { CreateSnippetModule } from './create-snippet.module';
import { ActivatedRoute } from '@angular/router';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { SnippetService } from '../shared/services/snippet.service';
import SpyObj = jasmine.SpyObj;
import { of } from 'rxjs';
describe('CreateSnippetComponent', () => {
let component: CreateSnippetComponent;
let fixture: ComponentFixture;
let snippetService: SpyObj;
let activatedRoute: Partial;
beforeEach(async(() => {
activatedRoute = {
snapshot: {
params: {}
}
} as any;
snippetService = jasmine.createSpyObj('snippetService', ['fetchPR']);
TestBed.configureTestingModule({
imports: [CreateSnippetModule, NoopAnimationsModule],
providers: [
{
provide: ActivatedRoute,
useFactory: () => activatedRoute
},
{
provide: SnippetService,
useValue: snippetService
}
]
}).compileComponents();
}));
beforeEach(() => {});
xit('should create', () => {
const repoName = 'name';
const repoOwner = 'PIKACHU';
const pullNumber = 689;
activatedRoute = {
snapshot: {
params: {
pullNumber,
repoName,
repoOwner
}
}
} as any;
snippetService.fetchPR.and.returnValue(of({}));
fixture = TestBed.createComponent(CreateSnippetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
expect(snippetService.fetchPR).toHaveBeenCalledWith(
repoName,
repoOwner,
pullNumber
);
});
});
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/create-snippet.component.ts
================================================
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
OnDestroy,
ViewChild
} from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import {
MatAutocomplete,
MatAutocompleteSelectedEvent
} from '@angular/material/autocomplete';
import { MatChipInputEvent } from '@angular/material/chips';
import { MatDialog } from '@angular/material/dialog';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { Observable } from 'rxjs/internal/Observable';
import { finalize, map } from 'rxjs/operators';
import { ReplaySubject } from 'rxjs/internal/ReplaySubject';
import { SnippetOverviewComponent } from './snippet-modal/snippet-overview.component';
import {
angularSampleCode,
LINKS_PLACEHOLDER,
MARKDOWN_PLACEHOLDER,
TAGS_LIST
} from '../shared';
import { SnippetService } from '../shared/services/snippet.service';
import {
markFormControlsAsTouched,
validatorMaxLines,
validatorMaxTags
} from '../shared/functions/validation';
import { parseSnippet } from '../shared/functions/parse-snippet';
import { SEPARATOR } from '../shared/consts';
interface SnippetFileInfo {
sha: string;
fileName: string;
snippet: string;
branchName: string;
}
function importSnippet(snippet) {
const result = { ...snippet };
result.links = (result.links || []).join(SEPARATOR);
return result;
}
@Component({
selector: 'codelab-create-snippet',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './create-snippet.component.html',
styleUrls: ['./create-snippet.component.scss']
})
export class CreateSnippetComponent implements OnDestroy {
@ViewChild('tagInput', { static: false }) tagInput: ElementRef<
HTMLInputElement
>;
@ViewChild('auto', { static: false }) matAutocomplete: MatAutocomplete;
repoName: string;
repoOwner: string;
destroy = new ReplaySubject(1);
isLoading = false;
isEditing = false;
snippetFileInfo: SnippetFileInfo;
TAGS_LIST = TAGS_LIST;
tags: string[] = ['tip'];
filteredTags: Observable;
snippetForm = this.fb.group({
title: ['', Validators.required],
twitter: [''],
level: ['beginner', Validators.required],
tags: [this.tags, [Validators.required, validatorMaxTags(5)]],
content: [
MARKDOWN_PLACEHOLDER,
[Validators.required, validatorMaxLines(25)]
],
bonus: [''],
links: [LINKS_PLACEHOLDER],
demo: [angularSampleCode]
});
hasBonus = false;
hasLinks = false;
hasDemo = false;
separatorKeysCodes: number[] = [ENTER, COMMA];
constructor(
private fb: FormBuilder,
private cd: ChangeDetectorRef,
private activatedRoute: ActivatedRoute,
private snippetService: SnippetService,
public dialog: MatDialog
) {
const pullNumber = this.activatedRoute.snapshot.params['pullNumber'];
this.repoName = this.activatedRoute.snapshot.params['repoName'];
this.repoOwner = this.activatedRoute.snapshot.params['repoOwner'];
if (pullNumber) {
this.isEditing = true;
this.fetchPR(pullNumber);
}
this.filteredTags = this.snippetForm
.get('tags')
.valueChanges.pipe(
map((tags: string) =>
tags ? this._filterTags(tags.slice(-1)[0]) : this.TAGS_LIST.slice()
)
);
}
fetchPR(pullNumber: number) {
this.isLoading = true;
this.snippetService
.fetchPR(this.repoName, this.repoOwner, pullNumber)
.pipe(
finalize(() => {
this.isLoading = false;
this.cd.markForCheck();
})
)
.subscribe((snippetFileInfo: SnippetFileInfo) => {
this.snippetFileInfo = snippetFileInfo;
const snippet = importSnippet(parseSnippet(snippetFileInfo.snippet));
if (snippet.demo) {
this.hasDemo = true;
}
if (snippet.bonus) {
this.hasBonus = true;
}
if (snippet.links) {
this.hasLinks = true;
}
this.snippetForm.patchValue(snippet);
});
}
ngOnDestroy() {
this.destroy.next(null);
this.destroy.complete();
}
openPreview() {
if (this.snippetForm.valid) {
this.dialog.open(SnippetOverviewComponent, {
data: {
formValue: this.getPreparedFormValue(this.snippetForm.value),
isEditing: this.isEditing,
fileInfo: this.snippetFileInfo
? {
sha: this.snippetFileInfo['sha'],
fileName: this.snippetFileInfo['fileName'],
branchName: this.snippetFileInfo['branchName']
}
: null,
repoName: this.repoName,
repoOwner: this.repoOwner
}
});
} else {
markFormControlsAsTouched(this.snippetForm);
}
}
getPreparedFormValue(value) {
Object.keys(value['demo']).forEach(x => {
const isChangedAndNotEmpty =
this.hasDemo && value.demo[x] && value.demo[x] !== angularSampleCode[x];
value['demo'][x] = isChangedAndNotEmpty ? value.demo[x] : null;
});
value['bonus'] = this.hasBonus ? value['bonus'] : null;
value['links'] = this.hasLinks ? value['links'] : null;
return value;
}
addTag(event: MatChipInputEvent): void {
if (!this.matAutocomplete.isOpen) {
const input = event.input;
const value = event.value;
if ((value || '').trim()) {
this.tags.push(value.trim());
}
if (input) {
input.value = '';
}
this.snippetForm.get('tags').patchValue(this.tags);
}
}
removeTag(tag: string): void {
const index = this.tags.indexOf(tag);
if (index >= 0) {
this.tags.splice(index, 1);
}
this.snippetForm.get('tags').patchValue(this.tags);
}
selectedTags(event: MatAutocompleteSelectedEvent): void {
this.tags.push(event.option.viewValue);
this.tagInput.nativeElement.value = '';
this.snippetForm.get('tags').patchValue(this.tags);
}
private _filterTags(value: string): string[] {
const filterValue = value ? value.toLowerCase() : null;
return this.TAGS_LIST.filter(
tag => tag.toLowerCase().indexOf(filterValue) === 0
);
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/create-snippet.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CreateSnippetComponent } from './create-snippet.component';
import { SnippetInfoComponent } from './snippet-info/snippet-info.component';
import { ReactiveFormsModule } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatButtonModule } from '@angular/material/button';
import { MatChipsModule } from '@angular/material/chips';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MarkdownModule } from 'ngx-markdown';
import { CodeDemoModule } from '@codelab/code-demos';
import { SnippetOverviewComponent } from './snippet-modal/snippet-overview.component';
import { SnippetSpinnerComponent } from './snippet-spinner/snippet-spinner.component';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { RouterModule } from '@angular/router';
const MAT_MODULES = [
MatButtonModule,
MatFormFieldModule,
MatChipsModule,
MatAutocompleteModule,
MatSelectModule,
MatInputModule,
MatDialogModule,
MatSnackBarModule
];
@NgModule({
declarations: [
CreateSnippetComponent,
SnippetInfoComponent,
SnippetOverviewComponent,
SnippetSpinnerComponent
],
entryComponents: [SnippetOverviewComponent],
imports: [
...MAT_MODULES,
CommonModule,
ReactiveFormsModule,
AngularFireAuthModule,
MarkdownModule.forRoot(),
CodeDemoModule,
RouterModule
]
})
export class CreateSnippetModule {}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-info/snippet-info.component.html
================================================
This form will generate a snippet request for you.
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-info/snippet-info.component.scss
================================================
:host {
.snippets-info {
float: right;
top: 40px;
max-width: 50%;
padding: 35px 35px 15px 35px;
margin-left: 30px;
}
.nav {
&-fixed-wrapper {
position: fixed;
right: 30px;
bottom: 30px;
z-index: 2000;
}
&-btn {
border-radius: 50%;
width: 45px;
height: 45px;
font-weight: bold;
display: table;
text-align: center;
box-shadow: 0 3px 7px rgba(0, 0, 0, 0.9);
&-go-up {
background: #213451;
color: white;
cursor: pointer;
}
p {
vertical-align: middle;
display: table-cell;
}
}
}
.animated {
animation-duration: 1s;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.fadeIn {
animation-name: fadeIn;
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-info/snippet-info.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-snippet-info',
templateUrl: './snippet-info.component.html',
styleUrls: ['./snippet-info.component.scss']
})
export class SnippetInfoComponent {}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-modal/snippet-overview.component.html
================================================
Snippet Markdown
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-modal/snippet-overview.component.scss
================================================
:host {
h1 {
color: #444;
}
.snippet-modal {
&-footer {
float: right;
}
}
.btn-submit {
float: right;
font-weight: 400;
text-align: center;
vertical-align: middle;
border: 1px solid #213451;
font-size: 14px;
color: #fff;
background-color: #213451;
cursor: pointer;
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-modal/snippet-overview.component.ts
================================================
import { Component, Inject, OnDestroy, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AngularFireAuth } from '@angular/fire/auth';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';
import { auth } from 'firebase/app';
import { finalize, switchMap, take, takeUntil } from 'rxjs/operators';
import { ReplaySubject } from 'rxjs/internal/ReplaySubject';
import { SnippetService } from '../../shared/services/snippet.service';
import { GitHubService } from '../../shared/services/github.service';
import { generateSnippet } from '../../shared/functions/generate-snippet';
import { SEPARATOR } from '../../shared/consts';
interface SnippetOverviewData {
formValue: object;
isEditing: boolean;
fileInfo: {
sha: string;
fileName: string;
branchName: string;
};
repoName: string;
repoOwner: string;
}
function exportSnippet(snippet) {
const result = { ...snippet };
result.links = result.links ? result.links.split(SEPARATOR) : undefined;
result.author = result.author || '** Your github username will be here **';
result.bonus = result.bonus || undefined;
return result;
}
@Component({
selector: 'codelab-snippet-overview',
templateUrl: './snippet-overview.component.html',
styleUrls: ['./snippet-overview.component.scss']
})
export class SnippetOverviewComponent implements OnInit, OnDestroy {
destroy = new ReplaySubject(1);
githubAuth;
isPRCreating = false;
isEditing: boolean;
snippet: string;
snippetWithFormat: string;
constructor(
public dialogRef: MatDialogRef,
private afAuth: AngularFireAuth,
private snippetService: SnippetService,
private githubService: GitHubService,
private _snackBar: MatSnackBar,
private router: Router,
@Inject(MAT_DIALOG_DATA) public data: SnippetOverviewData
) {}
ngOnInit() {
this.isEditing = this.data.isEditing;
this.snippet = generateSnippet(exportSnippet(this.data.formValue));
// This is a temporary hack.
// The version of markdown requires new lines between meta values, but github does not.
this.snippetWithFormat = this.snippet.replace(
/\n(title|author|twitter|level|tags|links):/g,
'\n\n$1:'
);
}
ngOnDestroy() {
this.destroy.next(null);
this.destroy.complete();
}
async onSubmit() {
console.log('You can copy the snippet here:\n', this.snippet);
this.isPRCreating = true;
if (!(this.githubAuth && this.githubAuth.credential)) {
await this.login();
}
if (this.isEditing) {
this.snippetService
.updatePR(
this.githubAuth,
this.snippet,
this.data.fileInfo,
this.data.repoName
)
.pipe(
finalize(() => (this.isPRCreating = false)),
takeUntil(this.destroy)
)
.subscribe(res =>
this.navigateAndShowSnackBar(
'Success',
'Snippet updated',
res['commit']['html_url']
)
);
} else {
this.snippetService
.createPR(
this.githubAuth,
this.snippet,
this.data.formValue['title'],
this.data.repoName,
this.data.repoOwner
)
.pipe(
switchMap(res =>
this.githubService.addLinkToEditForm(
this.data.repoOwner,
this.data.repoName,
res['number']
)
),
switchMap(res =>
this.githubService.addSnippetLabel(
this.data.repoOwner,
this.data.repoName,
res['number']
)
),
finalize(() => (this.isPRCreating = false)),
takeUntil(this.destroy)
)
.subscribe(res =>
this.navigateAndShowSnackBar(
'Pull request created',
res['title'].replace('Add - new snippet: ', ''),
res['html_url']
)
);
}
}
navigateAndShowSnackBar(text: string, linkLabel: string, linkUrl: string) {
this.dialogRef.close();
this.router.navigate(['list']);
const snakeBarRef = this._snackBar.open(text, linkLabel, {
duration: 20000
});
snakeBarRef
.onAction()
.pipe(take(1))
.subscribe(() => window.open(linkUrl));
}
async login() {
const provider = new auth.GithubAuthProvider().addScope('repo');
this.githubAuth = await this.afAuth.auth.signInWithPopup(provider);
this.data.formValue['author'] = this.githubAuth.additionalUserInfo.username;
this.snippet = generateSnippet(exportSnippet(this.data.formValue));
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-spinner/snippet-spinner.component.html
================================================
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-spinner/snippet-spinner.component.scss
================================================
.spinner-back-ground {
position: fixed;
width: 100%;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.2);
z-index: 9999;
}
.centered-spinner {
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
}
.preload {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/*change these sizes to fit into your project*/
width: 100px;
height: 100px;
}
.preload div {
border: 0;
margin: 0;
width: 40%;
height: 40%;
position: absolute;
border-radius: 50%;
animation: spin 2s ease infinite;
}
.preload :first-child {
background: #19a68c;
animation-delay: -1.5s;
}
.preload :nth-child(2) {
background: #f63d3a;
animation-delay: -1s;
}
.preload :nth-child(3) {
background: #fda543;
animation-delay: -0.5s;
}
.preload :last-child {
background: #193b48;
}
@keyframes spin {
0%,
100% {
transform: translate(0);
}
25% {
transform: translate(160%);
}
50% {
transform: translate(160%, 160%);
}
75% {
transform: translate(0, 160%);
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/create-snippet/snippet-spinner/snippet-spinner.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-snippet-spinner',
templateUrl: './snippet-spinner.component.html',
styleUrls: ['./snippet-spinner.component.scss']
})
export class SnippetSpinnerComponent {}
================================================
FILE: apps/angular-thirty-seconds/src/app/pull-requests-list/pull-requests-list.component.html
================================================
#
{{ item.number }}
Title
{{ item.title }}
Author
{{ item.user.login }}
Creation Date
{{ item.created_at | date: 'MM/dd/yyyy' }}
Action
Edit
loading ...
================================================
FILE: apps/angular-thirty-seconds/src/app/pull-requests-list/pull-requests-list.component.scss
================================================
h1 {
color: #444;
}
.container {
max-width: 850px;
padding: 0 15px 30px 15px;
margin: 0 auto;
font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
}
table {
width: 100%;
}
.mat-cell-padding {
padding: 0 5px;
}
.flex-header {
display: flex;
justify-content: space-between;
}
.align-center {
align-self: center;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/pull-requests-list/pull-requests-list.component.ts
================================================
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { GitHubService } from '../shared/services/github.service';
const REPO_OWNER = 'nycJSorg';
const REPO_NAME = '30-seconds-of-angular';
@Component({
selector: 'codelab-pull-requests-list',
templateUrl: './pull-requests-list.component.html',
styleUrls: ['./pull-requests-list.component.scss']
})
export class PullRequestsListComponent {
repoOwner = REPO_OWNER;
repoName = REPO_NAME;
pullsList$ = this.githubService.getPullsList(this.repoOwner, this.repoName);
displayedColumns = ['number', 'title', 'login', 'created_at', 'action'];
constructor(private router: Router, private githubService: GitHubService) {}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/angular-sample.ts
================================================
export const angularSampleCode = {
'app.component.ts': `import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: \`Edit me \`
})
export class AppComponent {}`,
'app.module.ts': `import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}`,
'main.ts': `import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
`,
'index.html': ' '
};
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/constants.ts
================================================
export const MARKDOWN_PLACEHOLDER = `
You can use markdown here.\n
Highlight \`important terms\` with backticks.\n
For examples use:
\`\`\`typescript
const language = 'English';
function theLanguageISpeak(language) {
// English? No, only typescript!
return 'typescript'
}
\`\`\``;
export const TAGS_LIST = [
'components',
'tip',
'forms',
'templates',
'styling',
'routing',
'performance'
];
export const LINKS_PLACEHOLDER = `https://angular.io/
https://www.typescriptlang.org/`;
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/consts.ts
================================================
export const SEPARATOR = '\n';
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/generate-snippet.spec.ts
================================================
import { testSnippetMd, testSnippetParsed } from './test-data/snippet';
import { generateSnippet } from './generate-snippet';
describe('GenerateSnippet', () => {
it('generates a simple snippet', () => {
const actual = generateSnippet(testSnippetParsed);
expect(actual).toEqual(testSnippetMd);
});
it('generates a snippet without demo', () => {
const testSnippet = { ...testSnippetParsed };
delete testSnippet.demo;
const actual = generateSnippet(testSnippet);
expect(actual).not.toContain('file:');
});
it('generates a snippet without bonus', () => {
const testSnippet = { ...testSnippetParsed };
delete testSnippet.bonus;
const actual = generateSnippet(testSnippet);
expect(actual).not.toContain('bonus');
});
it('generates a snippet without links', () => {
const testSnippet = { ...testSnippetParsed };
delete testSnippet.links;
const actual = generateSnippet(testSnippet);
expect(actual).not.toContain('links');
});
});
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/generate-snippet.ts
================================================
import { Snippet } from '../interfaces/snippet';
import { angularSampleCode } from '../angular-sample';
import { SEPARATOR } from '../consts';
const config = {
header: ['title', 'author', 'twitter', 'level', 'links', 'tags'],
body: ['content', 'bonus']
};
function arrayToMarkdownList(tagsArray: Array): string {
return tagsArray
.filter(a => a)
.map(x => `- ${x}`)
.join(`\n`);
}
function generateMdHeader(keys: string[], snippet: Snippet) {
return keys
.map(key => ({
key,
value: snippet[key]
}))
.filter(({ value }) => !!value)
.map(({ value, key }) => {
if (typeof value === 'string') {
return `${key}: ${value}`;
}
if (Array.isArray(value)) {
return `${key}:
${arrayToMarkdownList(value)}`;
}
throw new Error(key + 'is not a real key');
})
.join(SEPARATOR);
}
const ucFirst = s => {
if (typeof s !== 'string') {
return '';
}
return s.charAt(0).toUpperCase() + s.slice(1);
};
const extensionTolanguage = {
ts: 'typescript',
js: 'javascript'
};
function getFileLanguage(fileName) {
const fileExtension =
fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length) ||
fileName;
return extensionTolanguage[fileExtension] || fileExtension;
}
/**
* Drop markdown "```language```" from the code
*/
function addMarkdownLanguageMark(code: string, filename: string) {
return `\`\`\`${getFileLanguage(filename)}
${code}
\`\`\``;
}
function generateMdBody(keys: string[], snippet: Snippet) {
return keys
.map(key => ({
key,
value: snippet[key]
}))
.filter(({ value, key }) => !!value)
.map(({ value, key }) => {
return `# ${ucFirst(key)}
${value}
`;
})
.join(SEPARATOR);
}
function generateDemo(snippet) {
if (!snippet.demo) {
return '';
}
return (
Object.entries(snippet.demo)
.filter(([key, value]) => value && value !== angularSampleCode[key])
.map(([key, value]) => {
return `# file:${key}
${addMarkdownLanguageMark(value.toString(), key)}`;
})
.join(SEPARATOR) + SEPARATOR
);
}
export function generateSnippet(snippet: Snippet) {
const header = generateMdHeader(config.header, snippet);
const body = generateMdBody(config.body, snippet);
const demo = generateDemo(snippet);
return `---
${header}
---
${body}
${demo}`;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/parse-snippet.spec.ts
================================================
import { parseSnippet } from './parse-snippet';
import {
testSnippetEdgeCases,
testSnippetMd,
testSnippetMinimal,
testSnippetParsed
} from './test-data/snippet';
describe('ParseSnippet', () => {
it('parses a simple snippet', () => {
const actual = parseSnippet(testSnippetMd);
expect(actual).toEqual(testSnippetParsed);
});
it('testSnippetMinimal', () => {
const actual = parseSnippet(testSnippetMinimal);
expect(actual.bonus).toBe('');
expect(actual.links).toEqual(undefined);
expect(actual.demo).toEqual(undefined);
expect(actual.content).toEqual('Content');
});
it('works when file names have spaces', () => {
const actual = parseSnippet(testSnippetEdgeCases);
expect(actual.demo).toEqual({
'app.component.ts':
"import { Component } from '@angular/core';\n\n@Component({\n selector: 'my-app',\n template: `Edit me `\n})\nexport class AppComponent {}",
'app.module.ts':
"import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { AppComponent } from './app.component';\n" +
'\n@NgModule({\n imports: [BrowserModule],\n declarations: [AppComponent],\n' +
' bootstrap: [AppComponent]\n})\nexport class AppModule {}',
'main.ts':
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport { AppModule } from './app.module';" +
'\n\nplatformBrowserDynamic().bootstrapModule(AppModule);\n',
'index.html': ' ',
'app.svg': ' '
});
});
});
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/parse-snippet.ts
================================================
import { angularSampleCode } from '../angular-sample';
// @ts-ignore
// If you delete this you get a run time error.
// This is needed for gray-matter
window.Buffer = {
from() {}
};
// @ts-ignore
const matter = require('gray-matter');
/**
*
* Takes markdown and returns content.
* e.g. input:
*
* # LOL
* 1
* # HI
* 2
*
* result:
*
* {LOL: "1", HI: "2"}
*/
function extractHeaders(str) {
const match = ('\n' + str + '\n#').match(/\n#+.*\n[\s\S]*?(?=\n#)/g);
return !match
? { content: str }
: match.reduce((result, a) => {
const [, header, content] = a.match(/^\n#+(.*)\n([\s\S]*)$/);
result[header.trim().toLocaleLowerCase()] = content.trim();
return result;
}, {});
}
/**
*
* Takes markdown and returns content.
* e.g. input:
*
* ---
* title: Hello
* tags:
* - tips
* - good-to-know
* ---
*
* # LOL
* 1
* # HI
* 2
*
* result:
*
* {title: "Hello", tags: ["tips", "good-to-know"], LOL: "1", HI: "2"}
*
*/
function mdTextToJson(snippet: string) {
const metaData = matter(snippet);
return { ...extractHeaders(metaData.content), ...metaData.data };
}
/**
* Drop markdown "```language```" from the code
*/
function stripMarkdownLanguageMark(code = '') {
return code.replace(/```\w+\n/, '').replace(/\n```/, '');
}
function normalize(text) {
return text ? text.replace(/↵/g, '\n') : '';
}
export function parseSnippet(snippetBody: string) {
const snippet = mdTextToJson(snippetBody);
snippet.content = normalize(snippet.content);
snippet.bonus = normalize(snippet.bonus);
const demoFiles = Object.entries(snippet)
.filter(([key]) => key.startsWith('file:'))
.reduce((files, [key, value]) => {
files[key.replace(/^file:/, '').trim()] = stripMarkdownLanguageMark(
value.toString()
);
return files;
}, {});
if (Object.keys(demoFiles).length) {
snippet.demo = {
...angularSampleCode,
...demoFiles
};
}
return snippet;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/test-data/snippet.ts
================================================
export const testSnippetMd = `---
title: title
author: author
twitter: kirjs
level: intermediate
links:
- gogel.com
- 123.com
tags:
- tip
---
# Content
Content
# Bonus
Wow bonus
# file:app.component.ts
\`\`\`typescript
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.svg'
})
export class AppComponent {}
\`\`\`
# file:app.svg
\`\`\`svg
\`\`\`
`;
export const testSnippetParsed = {
content: 'Content',
bonus: 'Wow bonus',
'file:app.component.ts':
"```typescript\nimport { Component } from '@angular/core';\n @Component({\n selector: 'my-app',\n templateUrl: './app.svg'\n})\nexport class AppComponent {}\n```",
'file:app.svg': '```svg\n \n```',
title: 'title',
author: 'author',
twitter: 'kirjs',
level: 'intermediate',
links: ['gogel.com', '123.com'],
tags: ['tip'],
demo: {
'app.component.ts':
"import { Component } from '@angular/core';\n @Component({\n selector: 'my-app',\n templateUrl: './app.svg'\n})\nexport class AppComponent {}",
'app.module.ts':
"import { BrowserModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\nimport { AppComponent } from './app.component';\n" +
'\n@NgModule({\n imports: [BrowserModule],\n declarations: [AppComponent],\n bootstrap: [AppComponent]\n})\nexport class AppModule {}',
'main.ts':
"import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport { AppModule } from './app.module';\n\nplatformBrowserDynamic().bootstrapModule(AppModule);\n",
'index.html': ' ',
'app.svg': ' '
}
};
export const testSnippetMinimal = `---
title: title
author: author
twitter: kirjs
level: intermediate
tags:
- tip
---
# Content
Content`;
export const testSnippetEdgeCases = `---
title: title
author: author
twitter: kirjs
level: intermediate
tags:
- tip
---
# Content
Content
# file: app.svg
\`\`\`svg
\`\`\`
`;
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/validation/index.ts
================================================
export * from './validation';
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/functions/validation/validation.ts
================================================
import { FormArray, FormControl, FormGroup } from '@angular/forms';
import { AbstractControl } from '@angular/forms';
export function markFormControlsAsTouched(
formGroup: FormGroup | FormArray
): void {
Object.values(formGroup.controls).forEach(control => {
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
} else if (control instanceof FormGroup || control instanceof FormArray) {
markFormControlsAsTouched(control);
}
});
}
export function validatorMaxTags(maximumTags: number) {
return (control: AbstractControl) => {
return Array.isArray(control.value) && control.value.length > maximumTags
? { tagsError: `Number of tags should be below ${maximumTags + 1}` }
: null;
};
}
export function validatorMaxLines(lines: number) {
return (control: AbstractControl) => {
return control.value.split('\n').length > lines
? { linesError: `This field shouldn't have more than ${lines} lines` }
: null;
};
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/index.ts
================================================
export * from './angular-sample';
export * from './constants';
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/branch.interface.ts
================================================
export interface Branch {
ref: string;
node_id: string;
url: string;
object: {
type: string;
sha: string;
url: string;
};
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/commit-info.interface.ts
================================================
export interface CommitInfo {
message: string;
content: string;
branchName: string;
filePath: string;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/github-auth.interface.ts
================================================
import { User } from './user.interface';
export interface GithubAuth {
additionalUserInfo: {
profile: User;
};
credential: {
accessToken: string;
};
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/index.ts
================================================
export * from './branch.interface';
export * from './github-auth.interface';
export * from './repo.interface';
export * from './user.interface';
export * from './pull-request.intreface';
export * from './commit-info.interface';
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/pull-request.intreface.ts
================================================
export interface CreatePullRequest {
title: string;
body: string;
branchName: string;
labels?: Array;
}
export interface PullRequest {
title: string;
html_url: string;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/repo.interface.ts
================================================
export interface Repo {
name: string;
full_name: string;
sha: string;
url: string;
git_refs_url: string;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/snippet.ts
================================================
export type Snippet = Record;
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/interfaces/user.interface.ts
================================================
export interface User {
login: string;
repos_url: string;
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/services/github.service.ts
================================================
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Observable } from 'rxjs/internal/Observable';
import { catchError, map } from 'rxjs/operators';
import { throwError } from 'rxjs/internal/observable/throwError';
import { MonoTypeOperatorFunction } from 'rxjs/internal/types';
import {
Branch,
CommitInfo,
CreatePullRequest,
Repo,
User
} from '../interfaces';
// TODO work on github api names
// Here is an example link: https://github.com/github-tools/github
@Injectable({
providedIn: 'root'
})
export class GitHubService {
private apiGithubUrl = 'https://api.github.com';
private options: object;
constructor(private http: HttpClient, private _snackBar: MatSnackBar) {}
showSnackbarOnError(message: string): MonoTypeOperatorFunction {
return catchError(() => {
this._snackBar.open(message, '', { duration: 10000 });
return throwError(new Error(message));
});
}
setToken(token: string) {
this.options = { headers: { Authorization: `token ${token}` } };
}
getRepo(owner: string, repoName: string): Observable {
requires(owner, 'Owner is required');
requires(repoName, 'Repo name is required');
const requestUrl = `${this.apiGithubUrl}/repos/${owner}/${repoName}`;
return this.http
.get(requestUrl, this.options)
.pipe(this.showSnackbarOnError("Can't get repo"));
}
getMyRepos(user: User): Observable {
requires(user, 'User is required');
return this.http
.get(user.repos_url, this.options)
.pipe(this.showSnackbarOnError("Can't fetch user repos"));
}
forkRepo(repo: Repo): Observable {
requires(repo, 'Repository is required');
const requestUrl = `${this.apiGithubUrl}/repos/${repo.full_name}/forks`;
return this.http
.post(requestUrl, {}, this.options)
.pipe(this.showSnackbarOnError("Can't fork 30 secs repo"));
}
getMasterBranch(repo: Repo): Observable {
requires(repo, 'Repository is required');
const requestUrl = `${this.apiGithubUrl}/repos/${repo.full_name}/git/refs/heads/master`;
return this.http
.get(requestUrl, this.options)
.pipe(
this.showSnackbarOnError(
`Can't fetch master branch of ${repo.full_name}`
)
);
}
createBranch(
repo: Repo,
baseBranch: Branch,
branchName: string
): Observable {
requires(repo, 'Repository is required');
requires(baseBranch, 'Base branch is required');
requires(branchName, 'Branch name is required');
const requestUrl = `${this.apiGithubUrl}/repos/${repo.full_name}/git/refs`;
const branchRef = `refs/heads/${branchName}`;
const requestData = {
ref: branchRef,
sha: baseBranch.object.sha
};
return this.http
.post(requestUrl, requestData, this.options)
.pipe(
this.showSnackbarOnError(
`Can't create branch ${branchName} of base branch ${baseBranch.object.url}`
)
);
}
createCommit(repo: Repo, commitInfo: CommitInfo): Observable {
requires(repo, 'Repository is required');
requires(commitInfo, 'Commit is required');
const requestUrl = `${this.apiGithubUrl}/repos/${repo.full_name}/${commitInfo.filePath}`;
const requestData = {
message: commitInfo.message,
branch: commitInfo.branchName,
content: commitInfo.content
};
return this.http
.put(requestUrl, requestData, this.options)
.pipe(this.showSnackbarOnError("Can't create commit"));
}
createPullRequest(
repo: Repo,
user: User,
pullRequest: CreatePullRequest
): Observable {
requires(repo, 'Repository is required');
requires(user, 'User is required');
requires(pullRequest, 'Pull request is required');
const requestUrl = `${this.apiGithubUrl}/repos/${repo.full_name}/pulls`;
const requestData = {
title: pullRequest.title,
head: `${user.login}:${pullRequest.branchName}`,
base: 'master',
body: pullRequest.body,
labels: pullRequest.labels
};
return this.http
.post(requestUrl, requestData, this.options)
.pipe(this.showSnackbarOnError("Can't create pull request"));
}
getPullsList(owner: string, repoName: string): Observable {
return this.http
.get(
`${this.apiGithubUrl}/repos/${owner}/${repoName}/pulls`,
this.options
)
.pipe(
map(res =>
res.filter(
x =>
x['labels'].length &&
x['labels'].map(y => y['name']).indexOf('snippet') > -1
)
),
this.showSnackbarOnError("Can't fetch user repos")
);
}
getPullByPullNumber(
owner: string,
repoName: string,
pullNumber: number
): Observable {
return this.http
.get(
`${this.apiGithubUrl}/repos/${owner}/${repoName}/pulls/${pullNumber}`,
this.options
)
.pipe(this.showSnackbarOnError("Can't get pull request"));
}
addLinkToEditForm(
owner: string,
repoName: string,
pullNumber: number
): Observable {
return this.http
.patch(
`${this.apiGithubUrl}/repos/${owner}/${repoName}/pulls/${pullNumber}`,
{
body: `Here you can edit snippet content: https://30.codelab.fun/new/${pullNumber}`
},
this.options
)
.pipe(this.showSnackbarOnError("Can't update pull request"));
}
addSnippetLabel(owner: string, repoName: string, issueNumber: number) {
return this.http
.patch(
`${this.apiGithubUrl}/repos/${owner}/${repoName}/issues/${issueNumber}`,
{ labels: ['snippet'] },
this.options
)
.pipe(this.showSnackbarOnError("Can't get issues list"));
}
getPullFileByPullNumber(
owner: string,
repoName: string,
pullNumber: number
): Observable {
return this.http
.get(
`${this.apiGithubUrl}/repos/${owner}/${repoName}/pulls/${pullNumber}/files`,
this.options
)
.pipe(this.showSnackbarOnError("Can't get pull request file"));
}
getSnippetBody(url) {
return this.http
.get(url, this.options)
.pipe(this.showSnackbarOnError("Can't get snippet body"));
}
updateFile(repoFullName, snippetData, fileInfo): Observable {
const requestUrl = `${this.apiGithubUrl}/repos/${repoFullName}/contents/${fileInfo['fileName']}`;
const requestPayload = {
message: `Snippet Update`,
content: btoa(snippetData),
sha: fileInfo['sha'],
branch: fileInfo['branchName']
};
return this.http
.put(requestUrl, requestPayload, this.options)
.pipe(this.showSnackbarOnError('Cannot update file'));
}
}
function requires(expression: any, message: string) {
if (!expression) {
throw new Error(message);
}
}
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/services/snippet.service.spec.ts
================================================
import { TestBed } from '@angular/core/testing';
import { SnippetService } from './snippet.service';
import { GitHubService } from './github.service';
import { of } from 'rxjs';
import SpyObj = jasmine.SpyObj;
describe('SnippetService', () => {
let gitHubService: SpyObj;
const repoName = '30seconds';
const repoOwner = 'PIKACHU';
const pullNumber = 689;
beforeEach(() => {
gitHubService = jasmine.createSpyObj('gitHubService', [
'getPullByPullNumber',
'getPullFileByPullNumber',
'getSnippetBody'
]);
TestBed.configureTestingModule({
providers: [
{
provide: GitHubService,
useValue: gitHubService
}
]
});
});
it('should be created', () => {
const snippet = 'pirojok';
const fileName = 'john';
const contents_url = 'LOL';
const branchName = 'branch';
const service: SnippetService = TestBed.inject(SnippetService);
gitHubService.getPullByPullNumber.and.returnValue(
of({
head: { ref: branchName }
})
);
const sha = 'sa sha';
gitHubService.getPullFileByPullNumber.and.returnValue(
of([
{
contents_url,
sha,
filename: fileName
}
])
);
gitHubService.getSnippetBody.and.returnValue(
of({
content: btoa(snippet)
})
);
service.fetchPR(repoName, repoOwner, pullNumber).subscribe(result => {
expect(result).toEqual({
branchName,
fileName,
sha,
snippet
});
});
});
});
================================================
FILE: apps/angular-thirty-seconds/src/app/shared/services/snippet.service.ts
================================================
import { Injectable } from '@angular/core';
import { combineLatest, Observable, of } from 'rxjs';
import { debounceTime, map, switchMap } from 'rxjs/operators';
import slugify from 'slugify';
import { GitHubService } from './github.service';
import {
Branch,
CommitInfo,
CreatePullRequest,
GithubAuth,
PullRequest,
Repo,
User
} from '../interfaces';
@Injectable({
providedIn: 'root'
})
export class SnippetService {
constructor(private githubService: GitHubService) {}
fetchPR(repoName: string, repoOwner: string, pullNumber: number) {
// todo move it to service later
const pr$ = this.githubService.getPullByPullNumber(
repoOwner,
repoName,
pullNumber
);
const file$ = this.githubService
.getPullFileByPullNumber(repoOwner, repoName, pullNumber)
.pipe(
switchMap(([file]) => {
return this.githubService.getSnippetBody(file['contents_url']).pipe(
map(res => {
const body = atob(res.content);
return {
...res[0],
body,
sha: file['sha'],
fileName: file['filename']
};
})
);
})
);
return combineLatest([file$, pr$]).pipe(
map(([file, pr]) => {
return {
sha: file['sha'],
fileName: file['fileName'],
snippet: file['body'] as string,
branchName: pr['head']['ref']
};
})
);
}
updatePR(
githubAuth: GithubAuth,
snippetData: string,
fileInfo: object,
repoName: string
): Observable {
this.githubService.setToken(githubAuth.credential.accessToken);
const user: User = githubAuth.additionalUserInfo.profile;
return this.githubService.getMyRepos(user).pipe(
switchMap((repos: Repo[]) => {
const repo = repos.find(r => r.name === repoName);
return this.githubService.updateFile(
repo.full_name,
snippetData,
fileInfo
);
})
);
}
createPR(
githubAuth: GithubAuth,
snippetData: string,
title: string,
repoName: string,
repoOwner: string
): Observable {
requires(githubAuth, 'Github auth is required');
requires(snippetData, 'Snippet is required');
requires(title, 'Snippet title is required');
this.githubService.setToken(githubAuth.credential.accessToken);
const branchName = `new_snippet_${this.toLowerCaseAndSlugify(title)}`;
const filePath = `contents/snippets/${this.toLowerCaseAndSlugify(
title
)}.md`;
const user: User = githubAuth.additionalUserInfo.profile;
return this.githubService.getRepo(repoOwner, repoName).pipe(
switchMap((baseRepo: Repo) => {
return this.githubService.getMyRepos(user).pipe(
switchMap((repos: Repo[]) => {
const repo = repos.find(r => r.name === repoName);
return repo
? of(repo)
: this.githubService.forkRepo(baseRepo).pipe(debounceTime(5000));
}),
switchMap((userRepo: Repo) => {
return this.githubService.getMasterBranch(userRepo).pipe(
switchMap((masterBranch: Branch) => {
return this.githubService.createBranch(
userRepo,
masterBranch,
branchName
);
}),
switchMap(() => {
const commit: CommitInfo = {
message:
'I have added awesome snippet. Look at my awesome snippet!',
content: btoa(snippetData),
branchName: branchName,
filePath: filePath
};
return this.githubService.createCommit(userRepo, commit);
}),
switchMap(() => {
const pullRequest: CreatePullRequest = {
title: `Add - new snippet: ${title}`,
body: 'Here is a new snippet. Hope you like it :)',
labels: ['snippet'],
branchName: branchName
};
return this.githubService.createPullRequest(
baseRepo,
user,
pullRequest
);
})
);
})
);
})
);
}
private toLowerCaseAndSlugify(str: string) {
return slugify(str.toLowerCase());
}
}
function requires(expression: any, message: string) {
if (!expression) {
throw new Error(message);
}
}
================================================
FILE: apps/angular-thirty-seconds/src/assets/.gitkeep
================================================
================================================
FILE: apps/angular-thirty-seconds/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: apps/angular-thirty-seconds/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: apps/angular-thirty-seconds/src/index.html
================================================
AngularThirtySeconds
================================================
FILE: apps/angular-thirty-seconds/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: apps/angular-thirty-seconds/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: apps/angular-thirty-seconds/src/styles.scss
================================================
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
html,
body {
margin: 0;
padding: 0;
}
================================================
FILE: apps/angular-thirty-seconds/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
================================================
FILE: apps/angular-thirty-seconds/tsconfig.app.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": []
},
"exclude": ["test.ts", "**/*.spec.ts"],
"include": ["**/*.ts"]
}
================================================
FILE: apps/angular-thirty-seconds/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["jasmine"]
}
}
================================================
FILE: apps/angular-thirty-seconds/tsconfig.spec.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"types": ["jasmine", "node"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["**/*.spec.ts", "**/*.d.ts"]
}
================================================
FILE: apps/angular-thirty-seconds/tslint.json
================================================
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [true, "attribute", "codelab", "camelCase"],
"component-selector": [true, "element", "codelab", "kebab-case"]
}
}
================================================
FILE: apps/blog/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11
================================================
FILE: apps/blog/jest.config.js
================================================
module.exports = {
name: 'blog',
preset: '../../jest.config.js',
coverageDirectory: '../../coverage/apps/blog/'
};
================================================
FILE: apps/blog/src/app/app.component.html
================================================
Angular Codelab Newsletter
================================================
FILE: apps/blog/src/app/app.component.scss
================================================
h2 {
color: white;
text-align: center;
height: 70px;
background-image: linear-gradient(to right, red, white);
width: 90%;
}
div {
width: 100%;
display: flex;
}
span {
margin: auto;
display: inline-block;
vertical-align: middle;
line-height: normal;
margin-top: 20px;
}
img {
height: 100px;
margin-left: auto;
}
================================================
FILE: apps/blog/src/app/app.component.spec.ts
================================================
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { RouterTestingModule } from '@angular/router/testing';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [AppComponent]
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'blog'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('blog');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain(
'Welcome to blog!'
);
});
});
================================================
FILE: apps/blog/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'blog';
}
================================================
FILE: apps/blog/src/app/app.module.ts
================================================
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { ReactiveFormsModule } from '@angular/forms';
import { MatCardModule } from '@angular/material/card';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSelectModule } from '@angular/material/select';
import { BrowserModule } from '@angular/platform-browser';
import { RouterModule, Routes } from '@angular/router';
import { MarkdownModule } from 'ngx-markdown';
import { environment } from '../../../../apps/codelab/src/environments/environment';
import { AppComponent } from './app.component';
import { FeedComponent } from './feed/feed.component';
import { FormComponent } from './form/form.component';
import { PostService } from './post.service';
import { PostComponent } from './post/post.component';
import { SinglePostComponent } from './single-post/single-post.component';
export const angularFire = AngularFireModule.initializeApp(
environment.firebaseConfig
);
const appRoutes: Routes = [
{ path: 'post/:id', component: PostComponent },
{ path: '', component: FeedComponent },
{ path: 'form', component: FormComponent }
];
@NgModule({
declarations: [
AppComponent,
FormComponent,
FeedComponent,
PostComponent,
SinglePostComponent
],
imports: [
BrowserModule,
MarkdownModule.forRoot(),
MatFormFieldModule,
MatSelectModule,
ReactiveFormsModule,
HttpClientModule,
AngularFireDatabaseModule,
angularFire,
MatCardModule,
AngularFireAuthModule,
MatSnackBarModule,
RouterModule.forRoot(appRoutes, { initialNavigation: 'enabled' })
],
providers: [PostService],
bootstrap: [AppComponent],
exports: [FormComponent]
})
export class AppModule {}
================================================
FILE: apps/blog/src/app/common.ts
================================================
export interface Post {
key?: string;
title: string;
author: string;
text: string;
date: string;
hidden: boolean;
}
================================================
FILE: apps/blog/src/app/feed/feed.component.html
================================================
Tell us what you have done!
================================================
FILE: apps/blog/src/app/feed/feed.component.scss
================================================
================================================
FILE: apps/blog/src/app/feed/feed.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FeedComponent } from './feed.component';
describe('FeedComponent', () => {
let component: FeedComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FeedComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FeedComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/blog/src/app/feed/feed.component.ts
================================================
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { PostService } from '../post.service';
import { Observable } from 'rxjs';
import { Post } from '../common';
import { map } from 'rxjs/operators';
@Component({
selector: 'codelab-feed',
templateUrl: './feed.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styleUrls: ['./feed.component.scss']
})
export class FeedComponent {
posts$: Observable;
constructor(private postService: PostService) {
this.posts$ = this.postService.repo$.snapshotChanges().pipe(
map(items => {
return items
.map(a => {
return {
...a.payload.val(),
key: a.payload.key
};
})
.reverse();
})
);
}
}
================================================
FILE: apps/blog/src/app/form/form.component.html
================================================
Tell us what's new
Submit
Back
================================================
FILE: apps/blog/src/app/form/form.component.scss
================================================
.container {
display: flex;
flex-direction: column;
width: 40%;
}
input,
textarea {
width: 600px;
}
.form-control {
margin: 2px;
}
textarea {
overflow: auto;
resize: vertical;
}
================================================
FILE: apps/blog/src/app/form/form.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormComponent } from './form.component';
describe('FormComponent', () => {
let component: FormComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FormComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/blog/src/app/form/form.component.ts
================================================
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { PostService } from '../post.service';
import { Router } from '@angular/router';
import { Post } from '../common';
@Component({
selector: 'codelab-form',
templateUrl: './form.component.html',
styleUrls: ['./form.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FormComponent {
title = new FormControl('', Validators.required);
author = new FormControl('', Validators.required);
text = new FormControl('', Validators.required);
date: Date;
post: Observable;
myform = new FormGroup({
title: this.title,
author: this.author,
text: this.text
});
statusMessage = '';
error = false;
constructor(
private http: HttpClient,
private postService: PostService,
private router: Router
) {}
onSubmit() {
const formValues: any = this.myform.getRawValue();
this.postService
.addPost(formValues)
.then(({ key }) => {
this.myform.reset();
this.router.navigateByUrl(`post/${key}`);
})
.catch(() => {
this.statusMessage = 'Error';
this.error = true;
});
}
}
================================================
FILE: apps/blog/src/app/post/post.component.html
================================================
Back
================================================
FILE: apps/blog/src/app/post/post.component.scss
================================================
.text {
overflow: hidden;
max-height: 100px;
}
================================================
FILE: apps/blog/src/app/post/post.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PostComponent } from './post.component';
describe('PostComponent', () => {
let component: PostComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PostComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/blog/src/app/post/post.component.ts
================================================
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Post } from '../common';
import { PostService } from '../post.service';
import { ActivatedRoute } from '@angular/router';
import { MarkdownModule } from 'ngx-markdown';
@Component({
selector: 'codelab-post',
templateUrl: './post.component.html',
styleUrls: ['./post.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PostComponent implements OnInit {
post$: Observable;
key: string;
constructor(
private postService: PostService,
private route: ActivatedRoute
) {}
ngOnInit() {
this.key = this.route.snapshot.params['id'];
this.post$ = this.postService.getPost(this.key);
}
}
================================================
FILE: apps/blog/src/app/post.service.ts
================================================
import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
import { Observable } from 'rxjs';
import { Post } from './common';
import { database } from 'firebase/app';
@Injectable({
providedIn: 'root'
})
export class PostService {
repo$: AngularFireList = this.database.list('/posts', ref => {
return ref.orderByChild('hidden').equalTo(null);
});
constructor(private database: AngularFireDatabase) {}
getPostById(id: string) {
return this.database.object(`posts/${id}`);
}
removePost(id: string) {
return this.getPostById(id).remove();
}
updatePost(id: string, post: Partial) {
return this.getPostById(id).update(post);
}
addPost(post: Post): any {
return this.repo$.push({
...post,
date: database.ServerValue.TIMESTAMP as string
});
}
getPost(id: string): Observable {
return this.getPostById(id).valueChanges();
}
}
================================================
FILE: apps/blog/src/app/single-post/single-post.component.html
================================================
{{ post.title }}
by {{ post.author }} on
{{ post.date | date: 'short' }}
================================================
FILE: apps/blog/src/app/single-post/single-post.component.scss
================================================
.date {
float: right;
margin-right: 5px;
}
.title {
margin-top: 0px;
margin-top: 0px;
padding-left: 5px;
}
mat-card {
margin-bottom: 10px;
padding-bottom: 10px;
}
.text {
overflow: hidden;
max-height: 100px;
}
.gradient {
position: absolute;
height: 130px;
bottom: 0;
width: 100%;
}
.container {
position: relative;
}
================================================
FILE: apps/blog/src/app/single-post/single-post.component.ts
================================================
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { AccessService } from '../../../../codelab/src/app/shared/services/access.service';
import { Post } from '../common';
import { PostService } from '../post.service';
import { Router } from '@angular/router';
import { MatSnackBar } from '@angular/material/snack-bar';
@Component({
selector: 'codelab-single-post',
templateUrl: './single-post.component.html',
styleUrls: ['./single-post.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SinglePostComponent {
@Input() post: Post;
@Input() full: boolean;
@Input() key = '';
constructor(
private postService: PostService,
private accessService: AccessService,
private router: Router,
private snackBar: MatSnackBar
) {}
delete() {
this.accessService.oldIsAdmin$.subscribe();
this.post.hidden = true;
this.postService
.updatePost(this.key, this.post)
.then(() => {
this.router.navigateByUrl(``);
})
.catch(err => {
this.snackBar.open(`ERR: ${err}`);
});
}
}
================================================
FILE: apps/blog/src/assets/.gitkeep
================================================
================================================
FILE: apps/blog/src/assets/fonts/droid-sans/Apache License.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: apps/blog/src/environments/environment.prod.ts
================================================
export const environment = {
production: true
};
================================================
FILE: apps/blog/src/environments/environment.ts
================================================
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment = {
production: false
};
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
================================================
FILE: apps/blog/src/index.html
================================================
Blog
================================================
FILE: apps/blog/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: apps/blog/src/polyfills.ts
================================================
/**
* This file includes polyfills needed by Angular and is loaded before the app.
* You can add your own extra polyfills to this file.
*
* This file is divided into 2 sections:
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main
* file.
*
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
*
* Learn more in https://angular.io/guide/browser-support
*/
/***************************************************************************************************
* BROWSER POLYFILLS
*/
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js'; // Run `npm install --save classlist.js`.
/**
* Web Animations `@angular/platform-browser/animations`
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
*/
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
/**
* By default, zone.js will patch all possible macroTask and DomEvents
* user can disable parts of macroTask/DomEvents patch by setting following flags
* because those flags need to be set before `zone.js` being loaded, and webpack
* will put import in the top of bundle, so user need to create a separate file
* in this directory (for example: zone-flags.ts), and put the following flags
* into that file, and then add the following code before importing zone.js.
* import './zone-flags.ts';
*
* The flags allowed in zone-flags.ts are listed here.
*
* The following flags will work for all browsers.
*
* (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
* (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
* (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
*
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
* with the following flag, it will bypass `zone.js` patch for IE/Edge
*
* (window as any).__Zone_enable_cross_context_check = true;
*
*/
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
*/
================================================
FILE: apps/blog/src/styles.scss
================================================
/* You can add global styles to this file, and also import other style files */
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
@font-face {
font-family: 'Droid Sans';
src: url('/assets/fonts/droid-sans/DroidSans.ttf') format('opentype');
}
@font-face {
font-family: 'Droid Sans Bold';
src: url('/assets/fonts/droid-sans/DroidSans-Bold.ttf') format('opentype');
}
body {
font-family: 'Droid Sans', 'Droid Sans Bold';
}
================================================
FILE: apps/blog/src/test-setup.ts
================================================
import 'jest-preset-angular';
================================================
FILE: apps/blog/tsconfig.app.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
},
"exclude": ["src/test-setup.ts", "**/*.spec.ts"]
}
================================================
FILE: apps/blog/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["node", "jest"]
},
"include": ["**/*.ts"],
"angularCompilerOptions": {
"enableIvy": true
}
}
================================================
FILE: apps/blog/tsconfig.spec.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["jest", "node"]
},
"files": ["src/test-setup.ts"],
"include": ["**/*.spec.ts", "**/*.d.ts"]
}
================================================
FILE: apps/blog/tslint.json
================================================
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [true, "attribute", "codelab", "camelCase"],
"component-selector": [true, "element", "codelab", "kebab-case"]
}
}
================================================
FILE: apps/codelab/browserslist
================================================
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11 # For IE 9-11 support, remove 'not'.
================================================
FILE: apps/codelab/extra-webpack.config.js
================================================
const webpack = require('webpack');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const findLoader = (webpackConfig, regex) => {
return webpackConfig.module.rules
.filter(rule => !!rule.use)
.find(rule => rule.use.find(it => !!it.loader && regex.test(it.loader)));
};
module.exports = (webpackConfig, cliConfig) => {
if (cliConfig.buildOptimizer) {
const loader = findLoader(
webpackConfig,
/@angular-devkit\/build-optimizer.*\/webpack-loader/
);
const originalTest = loader.test;
loader.test = file => {
const isMonaco = !!file.match('node_modules/monaco-editor');
return !isMonaco && !!file.match(originalTest);
};
}
webpackConfig.plugins.push(new MonacoWebpackPlugin());
return webpackConfig;
};
================================================
FILE: apps/codelab/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '../',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../coverage'),
reports: ['html', 'lcovonly'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false
});
};
================================================
FILE: apps/codelab/src/app/admin/admin-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AdminComponent } from './admin.component';
import { UsersComponent } from './users/users.component';
import { FeedbackComponent } from './feedback/feedback.component';
const routes = [
{
path: '',
component: AdminComponent,
children: [
{ path: 'users', component: UsersComponent },
{ path: 'feedback', component: FeedbackComponent }
]
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule {}
================================================
FILE: apps/codelab/src/app/admin/admin.component.css
================================================
:host {
display: block;
max-width: 1000px;
margin: 0 auto;
}
.wrapper {
margin-top: 20px;
}
================================================
FILE: apps/codelab/src/app/admin/admin.component.html
================================================
Angular Codelab 🔥 Admin
{{ link.name }}
================================================
FILE: apps/codelab/src/app/admin/admin.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AdminComponent } from './admin.component';
import { AdminModule } from './admin.module';
import { getMockAngularFireProviders } from '@codelab/utils/src/lib/testing/mocks/angular-fire';
import { RouterModule } from '@angular/router';
describe('AdminComponent', () => {
let component: AdminComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [AdminModule, RouterModule.forRoot([])],
providers: [...getMockAngularFireProviders()]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AdminComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/admin/admin.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.css']
})
export class AdminComponent {
readonly links = [
{ link: 'users', name: 'Users' },
{ link: 'feedback', name: 'Feedback' }
];
}
================================================
FILE: apps/codelab/src/app/admin/admin.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatTabsModule } from '@angular/material/tabs';
import { AdminComponent } from './admin.component';
import { AdminRoutingModule } from './admin-routing.module';
import { FeedbackModule } from './feedback/feedback.module';
import { UsersModule } from './users/users.module';
@NgModule({
imports: [
AdminRoutingModule,
CommonModule,
FeedbackModule,
UsersModule,
MatCardModule,
MatTabsModule
],
declarations: [AdminComponent]
})
export class AdminModule {}
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback-message-table/feedback-message-table.component.ts
================================================
import {
Component,
Input,
ViewChild,
Output,
EventEmitter,
ChangeDetectionStrategy
} from '@angular/core';
import { MatMenuTrigger } from '@angular/material/menu';
import { MatTableDataSource } from '@angular/material/table';
import { MatSort } from '@angular/material/sort';
import { Message } from '@codelab/feedback/src/lib/message';
const clearTags = (value: string) =>
value.replace(/<[^>]+>/g, '').replace(/Angular Codelab \/ /, '');
const clearAllTags = (values: Message[]): Message[] =>
values.map((m: Message) => ({
...m,
header: clearTags(m.header || 'No header')
}));
const sortingDataAccessor = (item, property) => {
switch (property) {
case 'timestamp':
return new Date(item.timestamp).toISOString();
default:
return item[property];
}
};
@Component({
selector: 'codelab-feedback-message-table',
templateUrl: './feedback-message-table.html',
styleUrls: ['./feedback-message-table.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FeedbackMessageTableComponent {
@ViewChild(MatSort, { static: true }) sort: MatSort;
@Input('dataSource')
set dataSourceSetter(values: Message[]) {
this.dataSource.data = clearAllTags(values);
this.dataSource.sortingDataAccessor = sortingDataAccessor;
this.dataSource.sort = this.sort;
}
dataSource = new MatTableDataSource([]);
closeReasons = [
{ name: '[Duplicate]', reason: '[Duplicate]' },
{ name: '[No fix]', reason: '[No fix]' },
{ name: '[Done]', reason: '[Done]' },
{ name: '[Nice message]', reason: '[Nice message, though not a real bug]' },
{ name: "[Can't reproduce]", reason: "[Can't reproduce]" }
];
tableColumns = ['comment', 'name', 'header', 'timestamp', 'actions'];
}
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback-message-table/feedback-message-table.css
================================================
.done-row {
background: #fcfcfc;
color: #666;
}
.mat-column-comment {
width: 55%;
padding-right: 5px;
}
.mat-column-name {
width: 10%;
padding-right: 5px;
}
.mat-column-header {
width: 20%;
padding-right: 5px;
}
.mat-column-timestamp {
width: 10%;
max-width: 120px;
padding-right: 5px;
}
.mat-column-actions {
width: 5%;
max-width: 20px;
}
table {
width: 100%;
overflow: hidden;
}
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback-message-table/feedback-message-table.html
================================================
comment
{{element.comment}}
name
{{element.name}}
header
{{ element.header}}
href
{{element.href}}
timestamp
{{element.timestamp|date}}
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback.component.css
================================================
.panel {
margin-bottom: 10px;
}
mat-form-field {
margin-left: 15px;
margin-top: 5px;
width: 120px;
}
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback.component.html
================================================
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FeedbackComponent } from './feedback.component';
import { FeedbackModule } from './feedback.module';
import { getMockAngularFireProviders } from '@codelab/utils/src/lib/testing/mocks/angular-fire';
import { GithubService } from './github.service';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
describe('FeedbackComponent', () => {
let component: FeedbackComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [FeedbackModule, NoopAnimationsModule],
providers: [
...getMockAngularFireProviders(),
{ provide: GithubService, useValue: {} }
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FeedbackComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback.component.ts
================================================
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
import { Message } from '@codelab/feedback/src/lib/message';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
type Filter = 'all' | 'done' | 'notDone';
type Grouping = 'nothing' | 'href' | 'name';
function groupBy(feedback: Array, grouping: Grouping) {
const result = feedback.reduce((comment, item) => {
const groupName = item[grouping];
comment[groupName] = comment[groupName] || [];
comment[groupName].push(item);
return comment;
}, {});
return Object.keys(result).map(key => ({ key, value: result[key] }));
}
function normalize(feedback: Array) {
return feedback.map(item => ({
...(item.payload && item.payload.val()),
key: item.key
}));
}
function group([feedback, grouping]) {
if (grouping === 'nothing') {
return [
{
key: 'Messages',
value: feedback
}
];
}
if (grouping === 'name' || grouping === 'href') {
return groupBy(feedback, grouping);
}
throw new Error('Unknown grouping: ' + grouping);
}
function filter([feedback, filterName, [fromDate, toDate]]) {
let result;
if (filterName === 'all') {
result = feedback;
}
if (filterName === 'done') {
result = feedback.filter(message => message.isDone);
}
if (filterName === 'notDone') {
result = feedback.filter(message => !message.isDone);
}
const fromMs = fromDate ? new Date(fromDate).getTime() : null;
const toMs = toDate ? new Date(toDate).getTime() + 86400000 : null; // add 24hrs to include the day of upper bound
result = result.filter(msg => {
const timestampMs = new Date(msg.timestamp).getTime();
return (
(fromMs ? timestampMs >= fromMs : true) &&
(toMs ? timestampMs <= toMs : true)
);
});
return result;
}
@Component({
selector: 'codelab-feedback',
templateUrl: './feedback.component.html',
styleUrls: ['./feedback.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FeedbackComponent implements OnInit {
messages$: Observable<{ key: string; value: Message }[]>;
filter$ = new BehaviorSubject('notDone');
dateFilter$ = new BehaviorSubject<[string, string]>(['', '']);
group$ = new BehaviorSubject('nothing');
datesForFilter = { dateFrom: '', dateTo: '' };
constructor(private database: AngularFireDatabase) {}
ngOnInit() {
const feedback$: AngularFireList = this.database.list('/feedback');
const filteredMessages$ = combineLatest([
feedback$.snapshotChanges().pipe(map(normalize)),
this.filter$,
this.dateFilter$
]).pipe(map(filter));
this.messages$ = combineLatest([filteredMessages$, this.group$]).pipe(
map(group)
);
}
changeDate(clearDates = false) {
if (clearDates) {
this.datesForFilter = { dateFrom: '', dateTo: '' };
}
this.dateFilter$.next([
this.datesForFilter.dateFrom || '',
this.datesForFilter.dateTo || ''
]);
}
clearDate() {
this.changeDate(true);
}
}
================================================
FILE: apps/codelab/src/app/admin/feedback/feedback.module.ts
================================================
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { MatMenuModule } from '@angular/material/menu';
import { MatButtonModule } from '@angular/material/button';
import { MatSelectModule } from '@angular/material/select';
import { MatTableModule } from '@angular/material/table';
import { MatIconModule } from '@angular/material/icon';
import { MatDatepickerModule } from '@angular/material/datepicker';
import { MatInputModule } from '@angular/material/input';
import { MatExpansionModule } from '@angular/material/expansion';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatSortModule } from '@angular/material/sort';
import { MatNativeDateModule } from '@angular/material/core';
import { SlidesModule } from '@ng360/slides';
import { BrowserWindowModule } from '@codelab/browser';
import { FeedbackMessageTableComponent } from './feedback-message-table/feedback-message-table.component';
import { FeedbackComponent } from './feedback.component';
import { FeedbackModule as FeedbackLibModule } from '@codelab/feedback';
@NgModule({
imports: [
RouterModule,
AngularFireDatabaseModule,
AngularFireAuthModule,
BrowserWindowModule,
FormsModule,
ReactiveFormsModule,
CommonModule,
SlidesModule,
MatButtonModule,
MatCardModule,
MatMenuModule,
MatSelectModule,
MatTableModule,
MatIconModule,
MatDatepickerModule,
MatNativeDateModule,
MatFormFieldModule,
MatInputModule,
MatExpansionModule,
MatSortModule,
FeedbackLibModule
],
declarations: [FeedbackComponent, FeedbackMessageTableComponent],
exports: [FeedbackComponent],
entryComponents: [FeedbackComponent]
})
export class FeedbackModule {}
================================================
FILE: apps/codelab/src/app/admin/feedback/github.service.ts
================================================
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class GithubService {
repo = 'codelab-fun/codelab';
constructor(private http: HttpClient) {}
createIssue(issueData, accessToken) {
const headers = { Authorization: 'token ' + accessToken };
const options = { headers };
return this.http.post(
`https://api.github.com/repos/${this.repo}/issues`,
issueData,
options
);
}
closeIssue(changes, issueId, accessToken) {
const headers = { Authorization: 'token ' + accessToken };
const options = { headers };
return this.http.patch(
`https://api.github.com/repos/${this.repo}/issues/${issueId}`,
changes,
options
);
}
}
================================================
FILE: apps/codelab/src/app/admin/users/users.component.css
================================================
table {
width: 100%;
}
================================================
FILE: apps/codelab/src/app/admin/users/users.component.html
================================================
Key
{{ admin.key }}
You
{{ admin.isCurrentUser ? '⛱' : '' }}
================================================
FILE: apps/codelab/src/app/admin/users/users.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UsersComponent } from './users.component';
import { UsersModule } from './users.module';
import { SyncDbService } from '@codelab/utils/src/lib/sync/services/sync-db.service';
import { of } from 'rxjs';
import { LoginService } from '@codelab/firebase-login';
describe('UsersComponent', () => {
let component: UsersComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [UsersModule],
providers: [
{
provide: SyncDbService,
useValue: { list: () => ({ snapshots$: of([]) }) }
},
{
provide: LoginService,
useValue: {}
}
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UsersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/admin/users/users.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { LoginService } from '@codelab/firebase-login';
import { SyncDbService } from '@codelab/utils/src/lib/sync/services/sync-db.service';
import { map } from 'rxjs/operators';
import { firebaseToValuesWithKey } from '@codelab/utils/src/lib/sync/common';
import { combineLatest, Observable } from 'rxjs';
import { Permissions } from '../../shared/services/access.service';
export interface AdminDb {
key: string;
permissions: Record;
}
export interface Admin extends AdminDb {
isCurrentUser: boolean;
}
export interface UserDb {
admin: AdminDb[];
}
@Component({
selector: 'codelab-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.css']
})
export class UsersComponent {
readonly displayedColumns = ['isCurrentUser', 'key'];
readonly admins = this.dbService.list('admin');
private readonly allAdmins$ = this.admins.snapshots$.pipe(
map(firebaseToValuesWithKey)
);
readonly admins$: Observable = combineLatest([
this.allAdmins$,
this.loginService.uid$
]).pipe(
map(([admins, currentUserUid]) => {
return admins.map(admin => ({
...admin,
isCurrentUser: admin.key === currentUserUid
}));
})
);
constructor(
private readonly loginService: LoginService,
private readonly dbService: SyncDbService
) {}
}
================================================
FILE: apps/codelab/src/app/admin/users/users.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { UsersComponent } from './users.component';
import { MatCardModule } from '@angular/material/card';
import { MatTableModule } from '@angular/material/table';
@NgModule({
declarations: [UsersComponent],
entryComponents: [UsersComponent],
imports: [CommonModule, MatTableModule, MatCardModule]
})
export class UsersModule {}
================================================
FILE: apps/codelab/src/app/app-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './components/login/login.component';
import { NotFoundComponent } from './components/not-found/not-found.component';
import { AdminGuard } from './shared/services/guards/admin-guard';
import { LoginGuard } from './shared/services/guards/login-guard';
const routes: Routes = [
{
path: '',
loadChildren: () =>
import('./codelabs/codelabs.module').then(m => m.CodelabsModule)
},
{
path: 'login',
component: LoginComponent,
canActivate: [LoginGuard]
},
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canActivate: [AdminGuard]
},
{
path: 'sync',
loadChildren: () =>
import('./sync/sync.module').then(m => m.SyncAdminModule)
},
{ path: '**', component: NotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
================================================
FILE: apps/codelab/src/app/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-root',
template: ' '
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/app.module.ts
================================================
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { monacoReady } from '@codelab/code-demos';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { LoginModule } from './components/login/login.module';
import { menuRoutes } from './codelabs/angular/angular-routing.module';
import { MENU_ROUTES } from './common';
import { environment } from '../environments/environment';
import { NotFoundModule } from './components/not-found/not-found.module';
import { MatButtonModule } from '@angular/material/button';
import { DirectivesModule } from './directives/directives.module';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
AppRoutingModule,
LoginModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
AngularFireModule.initializeApp(environment.firebaseConfig),
AngularFireAuthModule,
AngularFireDatabaseModule,
NotFoundModule,
MatButtonModule,
DirectivesModule
],
declarations: [AppComponent],
providers: [
{ provide: MENU_ROUTES, useValue: menuRoutes },
{ provide: APP_INITIALIZER, useValue: monacoReady, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/about/about.component.html
================================================
codelab.fun
What is codelab.fun
Online interactive angular tutorial no setup
All open source/Angular Ivy/on Github
Regular offline contributors meetings
20+ Live-events in 10+ countries
Live-Stream "Звуки программистов"
@kirjs and @thekiba_io meet on weekends and do long technical streams in
russian, https://www.twitch.tv/kirjs
Nov 11, 2016
Angular 2.1
Kirjs did not know Angular
Move exercises to the browser
Running tests (mocha / babel + traversing AST)
- The goal is to quickly assess whether user's code does what we want.
- Initially the plan was to run unit tests in an iframe with TestBed, mocha
+ chai (code TBD)
Types
There's no types (TODO: Explain)
Next level
Creating slides library with Angular
What's an ideal API for a presentation?
Can we use ng-template?
Can we use ng-template?
Highlights
We need a popup which
plays well with Monaco
Updates when the code is updated
Not obstructive
We don't. We use highlight instead
We don't. We use highlight instead
Find position by regex or string
Add classes using Monaco's deltaDecorations
Storing code samples
This can't be hard!
Can we use multiline strings?
Custom interpolation to the rescue?
Can we use ng-template?
Can we use ngNonBindable?
Can we use Script tag?
Can we use Textarea?
Textarea + ngNonBindable
Code in the component
raw-loader - code in files
Can we use figure out the types now?
Let's fake it!
Then webstorm started shouting at me.
Let's fake it!
So we kinda ready for prime time, except nothing really works
People will probably get really mad?
Let's add feedback feature
We use firebase
We want the feedback to be as specific as possible, so we need to identify
the slide
We Already use routing, and each slide has a #
We can't generate and ID
We have to give an ID manually
TADA
i18n
We use standard Angular i18n
But how can we translate code samples?
Have strings in template with ids
Then query it in the Component
{{ "@ViewChild('translations', { static: true }) translation;" }}
How to we translate it in a way that scales?
Thanks PoEditor for providing us with an Open Source license!
Anybody can help with translation
- Already translated into Russian
Let's make it faster
- изначальные проблемы:
очень долго пересобиралось, потому что пересоздавался iframe
типы задавались вручную, поэтому было проблемно подключать дополнительные
библиотеки
- что сделано:
iframe стал переиспользоваться
инстанс ts compiler был переведен на watch режим
реализован bundler, который собирает типы в один файл
оптимизация бандла с пакетами, чтобы все сжималось
- что получили:
перезапуск вместо 0.5-1 сек стало почти мгновенно (показать демонстрацию)
размер бандла уменьшился в 2 раза (todo: посмотреть размер)
появилась возможность подключать любые библиотеки на этапе компиляции
Running tests (mocha / babel + traversing AST)
- The goal is to quickly assess whether user's code does what we want.
- Initially the plan was to run unit tests in an iframe with TestBed, mocha
+ chai (code TBD)
- Phase 2 - We started reusing iframe (code TBD)
- Special case testing bootstrapping the app (code TBD)
- We also added babel tests (babel can parse TypeScript) (code TBD)
- This works, we look at the code, not at the result.
Monaco
- The goal is to give users a type aware editor with SyntaxHighlighting
quick, extensible
- We chose monaco
- Bundle size/It's own AMD loader/Have to be included in the assets
- Adding types to monaco
- Highlighting
- Autofolding
- Init on app load
Experiments/What's next?
Sync lib
- Codelab is not just an angular course it is a platform
- Demo
New runner
- What we want:
- Generic runner (not just angular)
- Does not need types/libraries bundled (fetched them asynchronously from
cdn)
- Where we are?
- We don't know how to
How/Why to contribute?
Is is hard?
How to start
Why to contribute?
What's next?
================================================
FILE: apps/codelab/src/app/codelabs/about/about.component.scss
================================================
.screenshot {
/*background: url("images/sss");*/
}
[cover] ::ng-deep {
line-height: 100vh;
text-align: center;
background: #6cae00;
color: white;
font-size: 100px;
font-weight: 300;
}
[text] ::ng-deep {
line-height: 100vh;
text-align: center;
background: #6cae00;
color: white;
font-size: 100px;
font-weight: 300;
}
[pic] {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 100%;
}
[pic-label] ::ng-deep {
line-height: 20vh;
text-align: center;
color: black;
font-size: 100px;
font-weight: 280;
}
.types-error {
background-image: url('./images/types-error.png');
}
.types-missing {
background-image: url('./images/types-missing.png');
}
.types-work {
background-image: url('./images/types-work.png');
}
.i18n-poeditor {
background-image: url('./images/i18n-poeditor.png');
}
.highlight-slides {
background-image: url('./images/highlight-slides.png');
}
.img-todo {
content: 'lol';
border: 50px white dotted;
margin-top: 20px;
background: #d68200;
box-sizing: border-box;
&:before {
line-height: 100%;
font-size: 200px;
content: 'IMAGE-TBD';
padding: 100px 0;
text-align: center;
display: block;
color: white;
}
}
h2 {
padding: 20px;
}
.no-setup {
background-image: url('./images/no-setup.png');
}
.examples {
background-image: url('./images/examples.gif');
}
.exercises {
background-image: url('./images/exercises.gif');
}
.github {
background-image: url('./images/github.png');
}
.issues {
background-image: url('./images/issues.gif');
}
.community {
background-image: url('./images/codelab.jpeg');
}
================================================
FILE: apps/codelab/src/app/codelabs/about/about.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AboutComponent } from './about.component';
describe('AboutComponent', () => {
let component: AboutComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AboutComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AboutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/codelabs/about/about.component.ts
================================================
import { Component, OnInit } from '@angular/core';
declare const require;
@Component({
selector: 'about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent implements OnInit {
code = {
fakeTypes: require('!!raw-loader!./samples/fake-types.d.ts.not-really'),
slides: {
template: require('!!raw-loader!./samples/slides/ng-template.html'),
component: require('!!raw-loader!./samples/slides/slide-component.html'),
directive: require('!!raw-loader!./samples/slides/structural-directive.html')
},
storingCode: {
plain: require('!!raw-loader!./samples/storing-code/plain.html'),
backticks: require('!!raw-loader!./samples/storing-code/backticks.html'),
backticksMatch: [/{{`/, /`}}/],
interpolation: {
'bootstrap.ts': require('!!raw-loader!./samples/storing-code/interpolations.ts')
}
},
highlights: {
find: require('!!raw-loader!@codelab/code-demos/src/lib/code-demo-editor/utils/utils')
}
};
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/about/about.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { CodeDemoModule } from '@codelab/code-demos';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { AboutComponent } from './about.component';
const routes = RouterModule.forChild(SlidesRoutes.get(AboutComponent));
@NgModule({
declarations: [AboutComponent],
imports: [CommonModule, SlidesModule, routes, CodeDemoModule, FormsModule]
})
export class AboutModule {}
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/fake-types.d.ts.not-really
================================================
interface RouteConfig {
path: string;
component: any;
}
declare module '@angular/core' {
export class EventEmitter {
emit: (param: T) => void;
}
export interface DirectiveConfig {
selector: string;
}
export interface ComponentConfig {
selector: string;
template?: string;
templateUrl?: string;
}
export interface PipeConfig {
name: string;
}
export function enableProdMode();
export function Component(config: ComponentConfig);
export function Directive(config: DirectiveConfig);
export class TemplateRef {
}
export class ViewContainerRef {
clear: () => void;
createEmbeddedView: (ref: TemplateRef, context: any) => void;
}
export interface AfterViewInit {
ngAfterViewInit: () => void;
}
export interface OnInit {
ngOnInit: () => void;
}
export interface NgModuleConfig {
imports?: any[];
declarations?: any[];
providers?: any[];
bootstrap?: any[];
exports?: any[];
}
export function NgModule(config: NgModuleConfig);
export function Injectable();
export function Output();
export function Input();
export interface OnChanges {
ngOnChanges: (simpleChanges: SimpleChange[]) => void;
}
export interface SimpleChange {
[key: string]: any;
}
export function Pipe(config: PipeConfig);
export interface PipeTransform {
transform(value: string);
}
}
declare module '@angular/forms' {
export class FormsModule {
}
}
declare module '@angular/platform-browser' {
export class BrowserModule {
}
}
declare module '@angular/platform-browser/animations' {
export class NoopAnimationsModule {
}
}
declare module '@angular/platform-browser-dynamic' {
export class Platform {
bootstrapModule: (module: any, config?: any) => void;
}
export function platformBrowserDynamic(): Platform;
}
declare module '@angular/compiler' {
export class ResourceLoader {
}
}
declare class Observable {
subscribe: (T) => void;
}
declare module '@angular/router' {
export type Routes = Array;
export class RouterModule {
static forRoot: (routes: Routes) => any;
}
}
declare module '@angular/common' {
export class NgIf {
constructor(v: any, t: any);
}
}
declare module '@angular/material' {
export class MatTab {
position: number;
}
export class MatTabGroup {
selectedIndex: number;
selectChange: Observable;
}
export class MatInputModule {
}
export class MatTabsModule {
}
export class MatToolbarModule {
}
export class MatCardModule {
}
export class MatButtonModule {
}
}
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/slides/ng-template.html
================================================
Awesome slide
Code sample
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/slides/slide-component.html
================================================
Awesome slide
Code sample
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/slides/structural-directive.html
================================================
Awesome slide
Code sample
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/storing-code/backticks.html
================================================
{{` import { Component } from '@angular/core'; @Component({ selector:
'hello-world', template: `
Hello I'm an Angular app!
Very soon you will learn how to create and bootstrap me!
` }) export class AppComponent {} `}}
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/storing-code/interpolations.ts
================================================
import { Component, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
@Component({
selector: 'app-root',
template: `
🐶 1 + 2 🦊
`,
interpolation: ['🐶', '🦊']
})
export class AppComponent {}
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.error(err));
================================================
FILE: apps/codelab/src/app/codelabs/about/samples/storing-code/plain.html
================================================
import { Component } from '@angular/core'; @Component({ selector:
'hello-world', template: `
Hello I'm an Angular app!
Very soon you will learn how to create and bootstrap me!
` }) export class AppComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/angular-cli/angular-cli.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/angular-cli/angular-cli.component.html
================================================
Intro
Angular CLI is a command line tool that can be used to quickly get up to
speed with running your Angular app.
$ ng hello
Hi, I'm angular-cli , I'm a command line tool!
It may sound scary, but I'm really easy to use!
node
Before you start, make sure you have node.js on your machine.
Open terminal and type in: node -v
$ node -v v6.11.3
installing
Run npm install -g @angular/cli to install
Angular cli on your machine
$ sudo npm install -g @angular/cli
… A bunch of output omitted for brevety …
│ ├─┬ strip-ansi@3.0.1
│ │ └── ansi-regex@2.1.1
│ ├─┬ supports-color@3.2.3
│ │ └── has-flag@1.0.0
│ └─┬ yargs@6.6.0
│ ├── camelcase@3.0.0
│ ├── os-locale@1.4.0
│ ├─┬ string-width@1.0.2
│ │ ├── code-point-at@1.1.0
│ │ └─┬ is-fullwidth-code-point@1.0.0
│ │ └── number-is-nan@1.0.1
│ ├── which-module@1.0.0
│ └── yargs-parser@4.2.1
├── webpack-merge@4.1.0
├── webpack-sources@1.0.1
└── zone.js@0.8.18
Creating new Angular app
To create a new Angular app run:
ng new awesome-app, then cd awesome-app
$ ng new awesome-app
create awesome-app/README.md (1026 bytes)
create awesome-app/.angular-cli.json (1288 bytes)
create awesome-app/.editorconfig (245 bytes)
… A bunch of output omitted for brevety …
create awesome-app/src/app/app.component.spec.ts (986 bytes)
create awesome-app/src/app/app.component.ts (207 bytes)
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Successfully initialized git.
Project 'awesome-app' successfully created.
Starting the app
Once you're in the app folder, start the app with ng serve
>
$ ng serve
** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Date: 2017-10-22T03:25:09.430Z - Hash: 6af162d0da3cc77873d4
Time: 6417ms
chunk {{'{'}}inline{{'}'}} inline.bundle.js, inline.bundle.js.map (inline) 5.83 kB [entry] [rendered]
chunk {{'{'}}main{{'}'}} main.bundle.js, main.bundle.js.map (main) 8.63 kB {{'{'}}vendor{{'}'}} [initial]
[rendered]
chunk {{'{'}}polyfills{{'}'}} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 199 kB {{'{'}}inline{{'}'}}
[initial] [rendered]
chunk {{'{'}}styles{{'}'}} styles.bundle.js, styles.bundle.js.map (styles) 11.3 kB {{'{'}}inline{{'}'}} [initial]
[rendered]
chunk {{'{'}}vendor{{'}'}} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.32 MB [initial] [rendered]
webpack: Compiled successfully.
Once the app is started just open
http://localhost:4200/ in your
browser
Generating components
You can easily generate new components
ng generate component my-component
$ ng generate component my-component
create src/app/my-component/my-component.component.css (0 bytes)
create src/app/my-component/my-component.component.html (31 bytes)
create src/app/my-component/my-component.component.spec.ts (664 bytes)
create src/app/my-component/my-component.component.ts (292 bytes)
update src/app/app.module.ts (418 bytes)
You can also generate modules, services and pipes
You can use a shorter version: ng g c my-component
This is the end of the codelab, but it's just the beginning of
your Angular journey. Below are some links that can help you continue
learning.
Angular.io —
Find features, docs and events listed here
Angular CLI
makes it easy to create an application that already works, right
out of the box and generate new components! It also takes care of
the build system for you
================================================
FILE: apps/codelab/src/app/codelabs/angular/angular-cli/angular-cli.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-slides-angular-cli',
templateUrl: './angular-cli.component.html',
styleUrls: [
'../../../components/css/codelab-styles.scss',
'./angular-cli.component.css'
]
})
export class AngularCliComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/angular-cli/angular-cli.module.ts
================================================
import { AngularCliComponent } from './angular-cli.component';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FeedbackModule } from '@codelab/feedback';
import { CommonModule } from '@angular/common';
import { BrowserWindowModule } from '@codelab/browser';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
import { FormsModule } from '@angular/forms';
import { ExternalLinkDirectiveDirective } from '../../../components/external-link-directive/external-link-directive.directive';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
const routes = RouterModule.forChild([
...SlidesRoutes.get(AngularCliComponent)
]);
@NgModule({
imports: [
routes,
FeedbackModule,
CommonModule,
BrowserWindowModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
declarations: [AngularCliComponent, ExternalLinkDirectiveDirective],
exports: [AngularCliComponent]
})
export class AngularCliModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/angular-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { AngularRoutesComponent } from '../../components/angular-routes/angular-routes.component';
import { FullLayoutComponent } from '../../containers/full-layout';
import { environment } from '../../../environments/environment';
import { MENU_ROUTES, MenuRoutes } from '../../common';
const routes: MenuRoutes = [
{
path: '',
component: FullLayoutComponent,
children: [
{
path: '',
component: AngularRoutesComponent
},
{
path: 'intro',
loadChildren: () => import('@codelab/intro').then(m => m.IntroModule),
name: 'Introduction',
description: `Learn more about Angular`,
page: 'main',
prod: true
},
{
path: 'typescript',
loadChildren: () =>
import('./typescript/typescript.module').then(
m => m.TypeScriptModule
),
name: 'TypeScript',
description:
'Angular is written in TypeScript, a superset of JavaScript. Learn TypeScript',
page: 'typescript',
prod: true
},
{
path: 'create-first-app',
loadChildren: () =>
import('./create-first-app/create-first-app.module').then(
m => m.CreateFirstAppModule
),
name: 'Create your first Angular app',
description:
'Learn how to create and bootstrap your first Angular application',
page: 'main',
prod: true,
translationIds: ['createFirstNgApp', 'learnHowToBootstrapApp']
},
{
path: 'templates',
loadChildren: () =>
import('./templates/templates.module').then(m => m.TemplatesModule),
name: 'Templates',
description: 'Learn how to use Angular templates',
page: 'main',
prod: true,
translationIds: ['templates', 'learnUsingTemplates']
},
{
path: 'dependency-injection',
loadChildren: () =>
import('./dependency-injection/dependency-injection.module').then(
m => m.DependencyInjectionModule
),
name: 'Dependency-Injection',
description:
'Learn how to provide dependencies to your code instead of hard-coding them',
page: 'main',
prod: true,
translationIds: ['dependencyInjection', 'learnToProvideDependencies']
},
{
path: 'component-tree',
loadChildren: () =>
import('./component-tree/component-tree.module').then(
m => m.ComponentTreeModule
),
name: 'Component-Tree',
description: 'Learn how to structure your app with reusable components',
page: 'main',
prod: true,
translationIds: [
'componentTree',
'learnToStructureAppWithReusableComponents'
]
},
// {
// path: 'custom-events',
// loadChildren: './custom-events/custom-events.module#CustomEventsModule',
// name: 'Custom-Events (work in progress)',
// description: 'Learn to bind to events.',
// page: 'bonus',
// translationIds: ['customEvents', 'learnToBindToEvents']
// },
{
path: 'router',
loadChildren: () =>
import('./router/router.module').then(m => m.RouterCodelabModule),
name: 'Angular Router',
description: 'Learn how to add routes to your Angular application',
page: 'main',
prod: true
},
{
path: 'material',
loadChildren: () =>
import('./material/material.module').then(
m => m.MaterialCodelabModule
),
name: 'Angular Material',
description: 'Learn how to use Angular Material',
page: 'main',
prod: true
},
{
path: 'forms',
loadChildren: () =>
import('./forms/forms.module').then(m => m.FormsCodelabModule),
name: 'Forms',
description: 'Learn how to add Forms to your app',
page: 'main',
prod: true
},
{
path: 'angular-cli',
loadChildren: () =>
import('./angular-cli/angular-cli.module').then(
m => m.AngularCliModule
),
name: 'Angular-cli',
description: 'Learn how to quickly start working with angular',
page: 'main',
prod: true
},
{
path: 'pipes',
loadChildren: () =>
import('./pipes/pipes.module').then(m => m.PipesModule),
name: 'Pipes',
description:
'Learn how pipes transform input values to output values for display in a view',
page: 'bonus',
prod: false
},
{
path: 'structural-directives',
loadChildren: () =>
import('./structural-directives/structural-directives.module').then(
m => m.StructuralDirectivesModule
),
name: 'Structural Directives',
description: 'Learn about structural directives in angular',
page: 'bonus',
prod: true
},
{
path: 'playground',
loadChildren: () =>
import('./playground/playground.module').then(
m => m.PlaygroundModule
),
page: 'bonus',
prod: true
}
]
}
];
const isProd = environment.production;
export const menuRoutes = routes[0].children
.filter(x => x.page === 'main')
// Hide non-prod routes in prod
.filter(x => !isProd || x.prod);
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
providers: [{ provide: MENU_ROUTES, useValue: menuRoutes }]
})
export class AngularRoutingModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/angular.module.ts
================================================
import { NgModule } from '@angular/core';
import { AngularRoutingModule } from './angular-routing.module';
import { FullLayoutModule } from '../../containers/full-layout/full-layout.module';
import { AngularRoutesModule } from '../../components/angular-routes/angular-routes.module';
@NgModule({
imports: [AngularRoutingModule, FullLayoutModule, AngularRoutesModule]
})
export class AngularModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/component-tree.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/component-tree.component.html
================================================
video/video.component.ts: Add the '@Component' decorator and set its
selector property to 'my-video'.
app.module.ts: Add VideoComponent to the AppModule 'declarations'.
video/video.component.ts: Set the templateUrl to load the appropriate html
file
video/video.component.ts: Add a video property and decorate it with @Input()
video/video.component.html: Display the video title
video/video.component.html: Display the video thumbnail (src)
video/video.component.html: Display the video description
video/video.component.html: Display the video date
video/video.component.html: Display the number of video views
video/video.component.html: Display the number video likes
app.html: Replace existing title and thumbnail with our shiny new my-video
component
app.html: Use the data binding to pass the video object to the component
(don't forget the square brackets)
Component Tree
So far we have only one component, but as your app grows it will form a
tree of components
Parent and Child
Any component can render another one by using an HTML element that matches
the selector of the other component
Passing Data from Parent to Child
Parent component passes its data to the child component via properties
Change the size to 100 and color to red to
recreate the Japanese flag.
Passing Data from Parent to Child
The child class must decorate its properties with a special
@Input() decorator
This is the first time we're applying decorators to properties (as opposed
to classes).
Exercise 1
We already know how to create a component. Let's move all the
video-related information into a new component called VideoComponent.
We will bootstrap the component for you; the result will be as follows:
Cute kitten
Date 2016-11-25
Views 100
Likes 49329
Description todo
Parent and Child component
Components won't know about each other unless they're declared in the same
module
Exercise 2
In the next exercise you will use the newly created component
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/component-tree.component.ts
================================================
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import {
ExerciseConfigTemplate,
Ng2TsExercises,
SlideTemplate
} from '../../../../../../../ng2ts/ng2ts';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
import { boxAndCircle, circleAndBox } from '../../../shared/helpers/helpers';
const circleAndBoxCode = circleAndBox();
declare const require;
@Component({
selector: 'codelab-slides-component-tree',
templateUrl: './component-tree.component.html',
styleUrls: [
'../../../components/css/codelab-styles.scss',
'./component-tree.component.css'
]
})
export class ComponentTreeComponent implements AfterViewInit {
t: { [key: string]: string };
@ViewChild('translations', { static: false }) translation;
exercise: ExerciseConfigTemplate | SlideTemplate;
exercise2: ExerciseConfigTemplate | SlideTemplate;
title = 'Component Tree';
description = '';
prereqs = '';
code = {
parentComponentSkeleton: {
match: /slides-circle/,
code: `import { Component } from '@angular/core';
@Component({
selector: 'slides-hello',
template: \`Hello \`
})
export class HelloComponent {}
`,
path: 'parent.component.ts',
type: 'typescript'
},
childComponentSkeleton: {
code: `import { Component } from '@angular/core';
@Component({
selector: 'slides-world',
template: 'World '
})
export class WorldComponent {}
`,
path: 'child.component.ts',
type: 'typescript'
},
appModule: {
code: {
'app.module.ts': require('!!raw-loader!./samples/module/app.module.ts'),
'circle.component.ts': require('!!raw-loader!./samples/module/circle.component.ts'),
'box.component.ts': require('!!raw-loader!./samples/module/box.component.ts'),
'bootstrap.ts': require('!!raw-loader!./../../../shared/angular-code/bootstrap.ts'),
'index.html': require('!!raw-loader!./samples/module/index.html')
},
files: ['app.module.ts'],
highlights: { 'app.module.ts': /declarations.*/ }
},
parentComponent: {
code: `import { Component } from '@angular/core';
import { Result } from './result.model';
@Component({
selector: 'parent',
template: '
'
})
export class Parent {
results(): Result[] {...}
}`,
path: 'parent.component.ts',
type: 'typescript'
},
childComponent: {
code: `import { Component, Input } from '@angular/core';
import { Result } from './result.model';
@Component({
selector: 'child',
template: '{{result}}
'
})
export class Child {
@Input() data: Result[];
}`,
path: 'child.component.ts',
type: 'typescript'
},
boxAndCircle: boxAndCircle(),
circleAndBox: circleAndBoxCode,
passingDataToChildHighlights: {
'circle.component.ts': [/@Input\(\) size/, /@Input\(\) color/]
}
};
constructor(private exercises: Ng2TsExercises) {
this.exercise = exercises.getExercises(4, 1);
this.exercise2 = exercises.getExercises(4, 2);
}
ngAfterViewInit() {
this.t = extractMessages(this.translation);
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/component-tree.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { CodeDemoModule } from '@codelab/code-demos';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { ComponentTreeComponent } from './component-tree.component';
import { ComponentsHierarchySvgComponent } from './components-hierarchy-svg';
import { Ng2TsExercises } from '../../../../../../../ng2ts/ng2ts';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild([
...SlidesRoutes.get(ComponentTreeComponent)
]);
@NgModule({
imports: [
routes,
BrowserWindowModule,
CodeDemoModule,
FeedbackModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
providers: [Ng2TsExercises],
declarations: [ComponentTreeComponent, ComponentsHierarchySvgComponent],
exports: [ComponentTreeComponent]
})
export class ComponentTreeModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/components-hierarchy-svg/components-hierarchy-svg.component.html
================================================
Child
Child
Child
Parent
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/components-hierarchy-svg/components-hierarchy-svg.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentsHierarchySvgComponent } from './components-hierarchy-svg.component';
describe('ComponentsHierarchySvgComponent', () => {
let component: ComponentsHierarchySvgComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ComponentsHierarchySvgComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ComponentsHierarchySvgComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/components-hierarchy-svg/components-hierarchy-svg.component.ts
================================================
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'codelab-components-hierarchy-svg',
templateUrl: './components-hierarchy-svg.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ComponentsHierarchySvgComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/components-hierarchy-svg/index.ts
================================================
export * from './components-hierarchy-svg.component';
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/samples/module/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BoxComponent } from './box.component';
import { CircleComponent } from './circle.component';
@NgModule({
imports: [BrowserModule],
declarations: [BoxComponent, CircleComponent],
bootstrap: [BoxComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/samples/module/box.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
`
})
export class BoxComponent {
circleColor = 'green';
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/samples/module/circle.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'slides-circle',
template:
'
'
})
export class CircleComponent {
@Input() size: number;
@Input() color: string;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/component-tree/samples/module/index.html
================================================
Loading...
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/create-first-app.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/create-first-app.component.html
================================================
@Component is an Angular decorator
No semicolon here (as it attaches itself to the class below
The Decorator goes directly above the decorated entity (class in this case)
Component name is the class name (AppComponent).
Create a class called 'AppComponent'
Create a class called 'AppModule'
All set! Bootstrap your application
Export the class
Add a Component decorator for the class
Add a selector to the component decorator and set it to 'my-app'
Add a template that contains: h1 with a text "Hello MewTube!"
Add a NgModule decorator for the class
Add 'BrowserModule' to the NgModule decorator imports
Add 'AppComponent' to the 'declarations' property of the decorator
Add 'AppComponent' to the 'bootstrap' property of the decorator
What is Angular?
Angular is a development platform for building mobile and desktop
applications. Angular lets you extend HTML's syntax to express
your application's components clearly and succinctly. Angular's binding
and Dependency Injection eliminate much of the code you would
otherwise have to write.
Intro
Given an HTML file:
Let's create an Angular app which replaces the hello-world HTML
element with the app's contents.
This can be done with 3 simple steps.
Intro
The 3 steps are:
Create an Angular component
Create an Angular module
Bootstrap the module
Step 1
Start by creating an Angular Component . Components in Angular are
responsible for the visual part of the app
An Angular component is just a class. Properties and behavior can be added
inside.
Decorators
The class is adorned with a @Component decorator
Decorators attach Angular specific information to the class.
Decorators
Decorators are a new feature of TypeScript. They attach metadata to a
class, function, property or variable
TypeScript decorators are inspired by a similar feature in the Python
language.
Selector
Selectors define the location of the component. When Angular renders this
component, it'll find a hello-world HTML element in the document
and render the component inside of it
Inline Template
Template defines the HTML code that the component generates
If the amount of HTML grows out of hand, it's possible (and recommended)
to use a templateUrl instead and provide a path to the HTML file.
Exercise
In the next slide you'll create your first Angular component! We'll
do all the wiring for you. The result will look like this:
Create first Angular component!
Step 2
Next step is to declare the component in an NgModule .
NgModule does not have any visual representation and is used
exclusively for grouping Angular building blocks together
We will learn more about NgModules in the future milestones
Module Class
Like a component, Angular module is just a class
NgModule Decorator
Like a component, Angular module is adorned with a decorator
providing metadata
Browser Module
Declarations
The declarations array specifies components belonging to the
AppModule
Bootstrap
The component passed into the bootstrap array will be created and
displayed in your index.html file
Exercise
In the next slide you'll create your first Angular module! We'll
use the component from the previous exercises and do all the wiring for
you. The result will look like this:
Bootstrapping
We have everything ready, so now it's time to start (bootstrap) the app!
Passing your AppModule to the bootstrapModule method will
start up all the components from that module's bootstrap section
For most simple apps, you can just copy/paste the code above "as is"
Bootstrapping 1
How does bootstrapping work in Angular?
1. Kicks off execution environment. platformBrowserDynamic() tells
Angular that we are operating in the browser
Bootstrapping 2
2. Angular initializes the component from the bootstrap array in
app.module.ts (HelloWorldComponent in this case)
Bootstrapping 3
3. Angular looks in the document for an element matching the selector
defined in HelloWorldComponent ('hello-world' in our case)
and inserts the component inside that element
Exercise
All set! In the next page you'll bootstrap your first Angular app!
Now that we've got both NgModule and the component ready, let's
bootstrap the app!
Review
Loading order: index -> main -> app.module -> app.component
While Angular is loading, the contents of the element will stay the same
(Loading... ) in this case
Well done! This is the end of the milestone!
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/create-first-app.component.ts
================================================
import { Component, OnInit, ViewChild } from '@angular/core';
import { ng2tsConfig } from '../../../../../../../ng2ts/ng2ts';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
declare const require;
@Component({
selector: 'codelab-slides-create-first-app',
templateUrl: './create-first-app.component.html',
styleUrls: [
'../../../components/css/codelab-styles.scss',
'./create-first-app.component.css'
]
})
export class CreateFirstAppComponent implements OnInit {
t: { [key: string]: string };
// TODO(kirjs): we can't access tanslation in OnInit hook iwht static set to false
// need to consider changing how we set code
@ViewChild('translations', { static: true }) translation;
// Exercises
exercises = [
ng2tsConfig.milestones[1].exercises[1],
ng2tsConfig.milestones[1].exercises[2],
ng2tsConfig.milestones[1].exercises[3],
{
name: 'Create a component',
description: 'Create first Angular component!
',
files: [
{
bootstrap: false,
excludeFromTesting: false,
type: 'typescript',
path: 'app.component.ts',
template: `import {Component} from '@angular/core';\n\n`,
moduleName: 'app.component',
code: `import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: 'Hello MewTube! ',
})
export class AppComponent {
}
`,
solution: `import { Component } from '@angular/core';\n\n@Component({\n selector: 'my-app',\n
template: 'Hello MewTube! ',\n})\nexport class AppComponent {\n}\n`,
after: 'export function evalJs( js ){ return eval(js);}'
},
{
bootstrap: false,
excludeFromTesting: false,
type: 'typescript',
path: 'app.module.ts',
template: `import { BrowserWindowModule } from '@angular/platform-browser';\nimport { NgModule } from '@angular/core';\n
import { AppComponent } from './app.component';\n\n@NgModule({\n imports: [BrowserWindowModule],\n declarations: [AppComponent],
\n bootstrap: [AppComponent]\n})\nexport class AppModule {\n}\n`,
moduleName: 'app.module',
code: `import { BrowserWindowModule } from '@angular/platform-browser';\nimport {NgModule} from '@angular/core';\n
import { AppComponent } from './app.component';\n\n@NgModule({\n imports: [BrowserWindowModule],\n declarations: [AppComponent],
\n bootstrap: [AppComponent]\n})\nexport class AppModule {\n}\n`,
readonly: true,
collapsed: true
},
{
bootstrap: true,
excludeFromTesting: true,
type: 'typescript',
path: 'main.ts',
template: `import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport {AppModule} from './app.module';\n\n
const platform = platformBrowserDynamic();\nplatform.bootstrapModule(AppModule);\n`,
moduleName: 'main',
code: `import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';\nimport {AppModule} from './app.module';\n\n
const platform = platformBrowserDynamic();\nplatform.bootstrapModule(AppModule);\n`,
readonly: true,
collapsed: true
}
]
}
];
// tslint:disable:max-line-length TODO: Clean up exercises and remove this comment.
private code: any = {};
// tslint:enable:max-line-length
ngOnInit() {
this.t = extractMessages(this.translation);
this.code = {
indexHtml: {
'index.html': require('!!raw-loader!./samples/index-html/index.html'),
'bootstrap.ts': require('!!raw-loader!./samples/index-html/bootstrap.ts')
},
angularApp: {
'index.html': require('!!raw-loader!./samples/app-component/index.html'),
'bootstrap.ts': require('!!raw-loader!./samples/app-component/bootstrap.ts'),
'app.component.ts': require('!!raw-loader!./samples/app-component/app.component.ts'),
'app.module.ts': require('!!raw-loader!./samples/app-component/app.module.ts')
},
indexHtmlMatches: { 'index.html': // },
helloMatches: { 'app.component.ts': /hello-world/ },
componentMatches: { 'app.component.ts': /export.*/ },
decoratorsMatches: { 'app.component.ts': /@C[^]*?\)[^]/ },
selectorMatches: { 'app.component.ts': /selector.*'.*'/ },
templateMatches: { 'app.component.ts': /template: `[^]*?`[^]/ },
exportMatches: { 'app.module.ts': /export.*/ },
ngModuleMatches: { 'app.module.ts': /@N[^]*?\)[^]/ },
declarationsMatches: { 'app.module.ts': /declarations.*/ },
bootstrapMatches: { 'app.module.ts': /bootstrap.*/ },
bootstrapPlatformMatches: {
'bootstrap.ts': /platformBrowserDynamic\(\).*/
},
decorators: {
code: `import {Component} from '@angular/core';
// ${this.t.componentIsDecorator}
@Component({
// metadata
}) // ${this.t.noSemicolon}
export class AppComponent {
// ${this.t.decoratorGoesAboveEntity}
// ${this.t.componentNameIsClassName}
}`
}
};
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/create-first-app.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FeedbackModule } from '@codelab/feedback';
import { CommonModule } from '@angular/common';
import { BrowserWindowModule } from '@codelab/browser';
import { CodeDemoModule } from '@codelab/code-demos';
import { FormsModule } from '@angular/forms';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
import { ModeComponent } from './mode/mode.component';
import { CreateFirstAppComponent } from './create-first-app.component';
const routes = RouterModule.forChild([
...SlidesRoutes.get(CreateFirstAppComponent)
]);
@NgModule({
imports: [
routes,
FeedbackModule,
CommonModule,
CodeDemoModule,
BrowserWindowModule,
CodelabComponentsModule,
CodeDemoModule,
SlidesModule,
FormsModule
],
declarations: [CreateFirstAppComponent, ModeComponent],
exports: [CreateFirstAppComponent]
})
export class CreateFirstAppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/mode/mode.component.css
================================================
.mode {
background-size: cover;
cursor: pointer;
background-repeat: no-repeat;
width: 60px;
height: 40px;
padding: 5px;
margin-right: 10px;
filter: grayscale(100%);
}
.mode.mode-current {
filter: none;
}
.mode:hover {
border-bottom: 5px solid gray;
}
.mode-web {
background-image: url(./pics/browsericon.png);
}
.mode-mobile {
background-image: url(./pics/phoneicon.png);
}
.mode-vr {
background-image: url(./pics/vricon.png);
}
.angular {
width: 100px;
}
.module-options {
display: flex;
}
.box-row {
display: flex;
}
.box-row-fill {
width: 50vw;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/mode/mode.component.html
================================================
Because we're building a browser web app, we need to pass
BrowserModule to the imports array
With Angular we build mobile apps using NativeScript or Ionic.
With Angular you can build VR apps with A-FRAME or WEBGL.
Angular is not just for web apps anymore; you can also use it to create
native phone apps and even VR scenes.
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/mode/mode.component.spec.ts
================================================
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ModeComponent } from './mode.component';
describe('ModeComponent', () => {
let component: ModeComponent;
let fixture: ComponentFixture;
beforeEach(() => {
TestBed.configureTestingModule({
schemas: [NO_ERRORS_SCHEMA],
declarations: [ModeComponent]
});
fixture = TestBed.createComponent(ModeComponent);
component = fixture.componentInstance;
});
it('can load instance', () => {
expect(component).toBeTruthy();
});
it(`modes defaults to: ['web', 'mobile', 'vr']`, () => {
expect(component.modes).toEqual(['web', 'mobile', 'vr']);
});
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/mode/mode.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'codelab-slides-ng-module-mode',
templateUrl: './mode.component.html',
styleUrls: ['./mode.component.css']
})
export class ModeComponent implements OnInit {
modes = ['web', 'mobile', 'vr'];
mode = this.modes[0];
code = {
moduleAnatomy: {
// Module Anatomy - Milestone #1
code: `/* Imports */
@NgModule({
imports: [ BrowserModule ],
declarations: [ HelloWorldComponent ],
bootstrap: [ HelloWorldComponent ],
})
export class AppModule {}`,
codeMobile: `/* Imports */
@NgModule({
imports: [ NativeScriptModule ],
declarations: [ HelloWorldComponent ],
bootstrap: [ HelloWorldComponent ],
})
export class AppModule {}`,
codeVR: `/* Imports */
@NgModule({
imports: [ SomeMagicVRModule ],
declarations: [ HelloWorldComponent ],
bootstrap: [ HelloWorldComponent ],
})
export class AppModule {}`,
matches: {
exportClass: /export.*/,
ngModule: /@N[^]*?\)[^]/,
importsArr: /imports.*/
},
readonly: true,
path: 'module.anatomy.ts',
type: 'typescript'
}
};
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/samples/app-component/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'hello-world',
template: `
Hello I'm an Angular app!
Very soon you will learn how to create and bootstrap me!
`
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/samples/app-component/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/samples/app-component/bootstrap.ts
================================================
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/samples/app-component/index.html
================================================
Loading...
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/samples/index-html/bootstrap.ts
================================================
import { Component, NgModule } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { BrowserModule } from '@angular/platform-browser';
@Component({
// tslint:disable-next-line:component-selector
selector: 'hello-world',
template: `
Hello I'm an Angular app!
Very soon you will learn how to create and bootstrap me!
`
})
export class AppComponent {}
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule);
================================================
FILE: apps/codelab/src/app/codelabs/angular/create-first-app/samples/index-html/index.html
================================================
Loading...
================================================
FILE: apps/codelab/src/app/codelabs/angular/custom-events/custom-events.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/custom-events/custom-events.component.html
================================================
Custom Events / Intro
In this milestone, you will learn to use @Output() to communicate from
child to parent component and handle custom events.
Custom Events / Passing data from child to parent
To 'listen' to a child's event, we bind to the child's event
childDidSomething.
To let the parent know that there is an event. We emit an event from
the child.
Custom Events / Exercise - Intro
Let's implement a ThumbsUp and ThumbsDown button for the VideoComponent
you worked on before.
================================================
FILE: apps/codelab/src/app/codelabs/angular/custom-events/custom-events.component.ts
================================================
import { Component } from '@angular/core';
import { ng2tsConfig } from '../../../../../../../ng2ts/ng2ts';
@Component({
selector: 'codelab-slides-custom-events',
templateUrl: './custom-events.component.html',
styleUrls: ['./custom-events.component.css']
})
export class CustomEventsComponent {
code = {
exercise1a: {
// parent
code: `
import { Component } from '@angular/core';
@Component({
selector: 'parent',
template: \`
\`
})
export class ParentComponent {
parentDoSomething() {
console.log('child did something');
}
}
`,
path: 'test.ts',
type: 'typescript',
match: /childDidSomething/
},
exercise1b: {
// child
code: `
import { Component } from '@angular/core';
@Component({
selector: 'child',
template: \`
\`
})
export class ChildComponent {
@Output() childDidSomething = new EventEmitter();
buttonClicked() {
this.childDidSomething.emit();
}
}
`,
path: 'test.ts',
type: 'typescript',
match: ``
}
};
exercises = [ng2tsConfig.milestones[5].exercises[1]];
// constructor(private exercises: Ng2TsExercises) {
// // this.exercise = exercises.getExercises(4, 1);
// // this.exercise2 = exercises.getExercises(4, 2);
// }
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/custom-events/custom-events.module.ts
================================================
import { CustomEventsComponent } from './custom-events.component';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FeedbackModule } from '@codelab/feedback';
import { CommonModule } from '@angular/common';
import { BrowserWindowModule } from '@codelab/browser';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
import { FormsModule } from '@angular/forms';
import { CodeDemoModule } from '@codelab/code-demos';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
const routes = RouterModule.forChild([
...SlidesRoutes.get(CustomEventsComponent)
]);
@NgModule({
imports: [
routes,
FeedbackModule,
CommonModule,
BrowserWindowModule,
CodeDemoModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
declarations: [CustomEventsComponent],
exports: [CustomEventsComponent]
})
export class CustomEventsModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/dependency-injection/dependency-injection.component.css
================================================
.content-container {
display: flex;
flex-direction: row;
width: 100%;
margin-top: 20px;
}
.center-preview {
margin: 0 auto;
}
.mewtube-preview {
height: 100%;
padding: 2vw;
overflow: auto;
}
.comparision {
padding: 0.5em;
}
.diagram-container {
height: 500px;
width: 100%;
max-width: 800px;
margin: 2rem auto 0;
}
.di-diagram {
height: 100%;
width: 100%;
background: url(./pics/deps.svg) no-repeat;
background-size: contain;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/dependency-injection/dependency-injection.component.html
================================================
* TypeScript shorthand makes 'profession' * available to component instance.
assuming Job has property '.title'
video.service.ts: Add @Injectable() decorator to the class
app.module.ts: Add VideoService to the NgModule providers property
app.component.ts: Get rid of FAKE_VIDEOS
app.component.ts: Inject 'VideoService' in the component constructor as
'videoService'
app.component.ts: Update the app component's search method to use
videoService's search method
Example of a dependency
We display a list of hard-coded videos in our component, but in the real
world we'd load them from the server.
The code for fetching the data would live in a separate class (service)
called VideoService
Therefore our component will depend on the VideoService:
AppComponent
VideoService
Intro
As our app grows, number of dependencies will increase. Dependencies, in
order, will have their own dependencies. Managing all of them manually
becomes increasingly harder.
To simplify that Angular provides a mechanism called
Dependency injection
Comparison
Without Dependency Injection, Profession has to be instantiated
in the Person class
With Dependency Injection, Person class just "requires" an
instance of Job in the constructor, and Angular takes care of
instantiating it
Parameters
Without Dependency Injection, you have to figure out all the
parameters yourself
With Dependency Injection, Angular takes care of it
Testing
Also Dependency Injection simplifies testing a lot, because you can just
pass mock dependencies as constructor parameters
Example
Let's say we have an existing UnitConverterService and we want to
start using it in UnitConversionComponent . It will take 3 simple
steps:
Mark dependency as @Injectable()
Provide in the module
Require in the component
Step 1
Mark the class as @Injectable() . This lets Angular know that this
class is part of Angular Dependency Injection system
If a service class is marked as injectable, it can require other services
in its constructor.
Step 2
Provide the injectable to the providers section of NgModule
Now, this service becomes available for every Component and other
service in this NgModule .
Step 3
Consume the Injectable in the component
Because of the private access modifier the service becomes
accessible across the class as this.converter .
Exercise
In the next slide you'll use videoService which has even more cats!!! The
result will look like this:
================================================
FILE: apps/codelab/src/app/codelabs/angular/dependency-injection/dependency-injection.component.ts
================================================
import { Component, OnInit, ViewChild } from '@angular/core';
import { Ng2TsExercises } from '../../../../../../../ng2ts/ng2ts';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
@Component({
selector: 'codelab-slides-dependency-injection',
templateUrl: './dependency-injection.component.html',
styleUrls: ['./dependency-injection.component.css']
})
export class DependencyInjectionComponent implements OnInit {
t: { [key: string]: string };
exercise;
// TODO(kirjs): we can't access tanslation in OnInit hook iwht static set to false
// need to consider changing how we set code
@ViewChild('translations', { static: true }) translation;
code = {};
constructor(private exercises: Ng2TsExercises) {
this.exercise = exercises.getExercises(3, 1);
}
ngOnInit() {
this.t = extractMessages(this.translation);
this.code = {
withOutDI: {
code: `export class Person {
profession: Job;
constructor() {
this.profession = new Job();
}
}`,
code2: `import {ProfessionsEnum} from './professions';
export class Person {
profession: Job;
constructor() {
const Schedule = new Schedule(ProfessionsEnum.ENGINEER);
this.profession = new Job(ProfessionsEnum.ENGINEER, Schedule, /* TODO: Find how to inject salary*/);
}
}`,
matches: {
noDI: /this.*/
},
readonly: true,
path: 'person-noDI.ts',
type: 'typescript'
},
withDI: {
code: `export class Person {
/**
${this.t.shorthandMakesProfessionAvailable}
*/
constructor(public profession: Job) {}
}`,
matches: {
constructor: /constructor.*/
},
readonly: true,
path: 'personDI.ts',
type: 'typescript'
},
withDITesting: {
code: `const mockProfession = new Job('lawyer');
it('should create a Person with the right profession', () => {
const person = new Person(mockProfession);
// ${this.t.assumingJobHasPropTitle}
expect(person.profession.title).toEqual('lawyer');
});
`,
matches: {
constructor: /constructor.*/
},
readonly: true,
path: 'personDI.spec.ts',
type: 'typescript'
},
classAsInjectable: {
code: `import { Injectable } from '@angular/core';
@Injectable()
export class UnitConverterService {
// ...
}`,
matches: {
injectable: /@I[^]*?\)[^]/
},
readonly: true,
type: 'typescript'
},
provideInjectable: {
code: `import { NgModule } from '@angular/core';
import { UnitConverterService } from '../services/unit-converter.service';
import { UnitConversionComponent } from './unit-conversion.component';
@NgModule({
declarations: [ UnitConversionComponent ],
providers: [ UnitConverterService ]
})
export class AppModule {}`,
matches: {
providers: /providers.*/
},
readonly: true,
type: 'typescript'
},
consumeInjectable: {
code: `import { Component } from '@angular/core';
import { UnitConverterService } from '../services/unit-converter.service';
@Component({...})
export class UnitConversionComponent {
constructor(private converter: UnitConverterService) {}
}`,
matches: {
constructor: /constructor.*/
}
}
};
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/dependency-injection/dependency-injection.module.ts
================================================
import { NgModule } from '@angular/core';
import { DependencyInjectionComponent } from './dependency-injection.component';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { CodeDemoModule } from '@codelab/code-demos';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
import { Ng2TsExercises } from '../../../../../../../ng2ts/ng2ts';
const routes = RouterModule.forChild([
...SlidesRoutes.get(DependencyInjectionComponent)
]);
@NgModule({
imports: [
routes,
FeedbackModule,
BrowserWindowModule,
CodeDemoModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
providers: [Ng2TsExercises],
declarations: [DependencyInjectionComponent],
exports: [DependencyInjectionComponent]
})
export class DependencyInjectionModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/forms.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/forms.component.html
================================================
¬
app.module.ts: Add FormsModule and MatInputModule, to the imports.
upload/upload.component.html: Add a new input element
upload/upload.component.html: Bind the input to the title property using
ngModel
upload/upload.component.html: Add "description" textarea bound to the
description property of the component
Simple Form
We have this simple form, let's find out how to map input values with the
ones from our Angular component!
Simple Form
First we have to add FormsModule to our NgModule.
NgModel
Now let's use ngModel to bind the inputs to the appropriate fields on the
component.
Try changing the inputs and see the values updated.
[(NgModel)] - Banana in the box is a simple mnemonic for the braces
order.
Validators
Here's the list of
built-in validators
that Angular provides out of the box
min
max
required
requiredTrue
email
minLength
maxLength
pattern
It's also possible to create custom validators, but it's out of scope for
this milestone, you can read more:
here
Error Always Displayed
There's one small issue though, if we don't have initial values, the error
is displayed right away.
Exercise
In the next slide there is an exercise. We're going to add a form to the
upload page.
Note: you'd have to manually click on the "upload link" to see the result,
we're working on the fix.
Well done! This is the end of the milestone!
There are more than one way to handle forms in Angular. While
Advanced forms milestone is in works, check out
Angular docs
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/forms.component.ts
================================================
import { CodelabFile } from '../../../shared/helpers/codelabFile';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import {
ExerciseConfigTemplate,
Ng2TsExercises
} from '../../../../../../../ng2ts/ng2ts';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
declare const require;
interface FileHighlights {
appModule?: RegExp | RegExp[];
appHtml?: RegExp | RegExp[];
appComponent?: RegExp | RegExp[];
}
function formsConfig(code, highlights: FileHighlights = {}) {
const files = {
appHtml: require('!!raw-loader!./samples/basic/app.1.html'),
appModule: require('!!raw-loader!./samples/basic/app.module.ts'),
appComponent: require('!!raw-loader!./samples/basic/app.component.ts'),
...code
};
return {
files: [
CodelabFile.Html('app')
.setCode(files.appHtml)
.withHighlight(highlights.appHtml),
CodelabFile.TypeScriptFile('app.module')
.setCode(files.appModule)
.withHighlight(highlights.appModule),
CodelabFile.TypeScriptFile('app.component')
.setCode(files.appComponent)
.withHighlight(highlights.appComponent),
CodelabFile.TypeScriptFile('bootstrap')
.setCode(require('!!raw-loader!./samples/basic/main.ts'))
.makeBootstrappable(),
CodelabFile.Css('styles').setCode(
require('!!raw-loader!@angular/material/prebuilt-themes/indigo-pink.css')
),
CodelabFile.Css('extra').setCode(
require('!!raw-loader!./samples/basic/styles.css')
)
]
};
}
@Component({
selector: 'codelab-slides-forms',
templateUrl: './forms.component.html',
styleUrls: ['./forms.component.css']
})
export class FormsComponent implements AfterViewInit {
@ViewChild('translations', { static: false }) translations;
exercise: ExerciseConfigTemplate;
samples = {
basicForm: formsConfig(
{ appHtml: require('!!raw-loader!./samples/basic/app.1.html') },
{
appModule: /FormsModule]/
}
),
ngModel: formsConfig(
{ appHtml: require('!!raw-loader!./samples/basic/app.2.html') },
{
appHtml: [/ngModel/g]
}
),
ngValidation1: formsConfig(
{ appHtml: require('!!raw-loader!./samples/basic/app.3.html') },
{
appHtml: [/required/]
}
),
ngValidation2: formsConfig(
{ appHtml: require('!!raw-loader!./samples/basic/app.4.html') },
{
appHtml: [/#usernameModel="ngModel"/, //]
}
),
touched: formsConfig(
{
appHtml: require('!!raw-loader!./samples/basic/app.4.html'),
appComponent: require('!!raw-loader!./samples/basic/app.component.5.ts')
},
{
appComponent: /username = ''/
}
),
touched2: formsConfig(
{
appHtml: require('!!raw-loader!./samples/basic/app.5.html'),
appComponent: require('!!raw-loader!./samples/basic/app.component.5.ts')
},
{
appHtml: /(usernameModel.touched \|\| usernameModel.dirty)/
}
),
ngMaterial: formsConfig(
{
appHtml: require('!!raw-loader!./samples/basic/app.6.html'),
appModule: require('!!raw-loader!./samples/basic/app.module.6.ts').replace(
'component.5',
'component' /*Stupid hack*/
)
},
{
appHtml: [
/
/,
/<\/mat-form-field>/,
/matInput/,
/.*<\/mat-error>/
]
}
)
};
private t: Record;
constructor(private exercises: Ng2TsExercises) {
this.exercise = exercises.getExercises(7, 0);
}
ngAfterViewInit() {
this.t = extractMessages(this.translations);
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/forms.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { FormsComponent } from './forms.component';
import { Ng2TsExercises } from '../../../../../../../ng2ts/ng2ts';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild([...SlidesRoutes.get(FormsComponent)]);
@NgModule({
imports: [
routes,
FeedbackModule,
CommonModule,
BrowserWindowModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
declarations: [FormsComponent],
exports: [FormsComponent],
providers: [Ng2TsExercises]
})
export class FormsCodelabModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.1.html
================================================
*name:
email:
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.2.html
================================================
*name:
email:
Username: {{username}}
email: {{email}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.3.html
================================================
*name:
email:
Username: {{username}}
email: {{email}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.4.html
================================================
email:
Username: {{username}}
email: {{email}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.5.html
================================================
email:
Username: {{username}}
email: {{email}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.6.html
================================================
Username is required
Username: {{username}}
email: {{email}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.component.5.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
templateUrl: './app.html'
})
export class AppComponent {
username = '';
email = 'pirojok@angular.io';
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
templateUrl: 'app.html'
})
export class AppComponent {
username = 'Pirojok';
email = 'pirojok@angular.io';
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.html
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.module.6.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component.5';
import { FormsModule } from '@angular/forms';
import { MatInputModule } from '@angular/material/input';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@NgModule({
imports: [BrowserModule, NoopAnimationsModule, MatInputModule, FormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/code.ts
================================================
// I'm ignored
export const hi = 'hi';
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/main.ts
================================================
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { ResourceLoader } from '@angular/compiler';
import * as code from './code';
class MyResourceLoader extends ResourceLoader {
get(url: string): Promise {
const templateId = Object.keys(code).find(key =>
key.includes(url.replace(/[\/\.-]/gi, '_'))
);
const template = code[templateId];
if (!template) {
console.log(template);
// tslint:disable-next-line:no-debugger
debugger;
}
return Promise.resolve(template);
}
}
platformBrowserDynamic().bootstrapModule(AppModule, {
providers: [{ provide: ResourceLoader, useClass: MyResourceLoader, deps: [] }]
} as any);
================================================
FILE: apps/codelab/src/app/codelabs/angular/forms/samples/basic/styles.css
================================================
body {
padding: 0;
margin: 0;
}
.error {
color: red;
font-size: 12px;
margin-top: 6px;
}
.field {
padding: 10px;
border-bottom: 1px #ddd solid;
width: 200px;
}
.field input {
float: right;
}
.values {
margin-top: 20px;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/material.component.css
================================================
.examples {
width: 100%;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/material.component.html
================================================
¬
app.module.ts: Add MatCardModule and MatToolbarModule, to the imports.
app.html: Add material toolbar containing the title
video/video.component.html: Use material card to display the data
video/video.component.html: Add mat-card-title (inside of the mat-card) to
display video title
video/video.component.html: Add mat-card-subtitle (inside of the mat-card)
to display video description
video/video.component.html: Mark img with mat-card-image attribute (inside
of the mat-card) so that it takes full card size
video/video.component.html: move date/views/likes info inside of
mat-card-content (inside of the mat-card)
Now let's use
Angular Material
to make our app look beautiful ✨✨
Angular Material provides you with a set of
Material Design
components for Angular:
Fast and Consistent
Versatile
Accessible
Optimized for Angular
Look great on mobile
See the list of components
here
Card
Now let's add material card.
Themes
All Angular Material components allow to apply themes. Try different
themes by clicking the buttons below:
Indigo
Deep purple
Pink
Purple
Exercise
In the next slide there is an exercise. We're going to materialize our
application
Note: the final app won't be super beautiful, we're still working on that.
Well done! This is the end of the milestone!
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/material.component.ts
================================================
import {
AfterViewInit,
Component,
ViewChild,
ViewEncapsulation
} from '@angular/core';
import {
ExerciseConfigTemplate,
Ng2TsExercises
} from '../../../../../../../ng2ts/ng2ts';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
import { CodelabFile } from '../../../shared/helpers/codelabFile';
declare const require;
interface FileHighlights {
appModule?: RegExp;
appHtml?: RegExp;
}
function matExercise(
modules,
html,
highlights: FileHighlights = {},
theme = 'purple'
) {
const moduleCode = require('!!raw-loader!./samples/basic/app.module.ts').replace(
/MatCardModule, MatToolbarModule/g,
modules
);
return {
files: [
CodelabFile.TypeScriptFile('app.module')
.setCode(moduleCode)
.withHighlight(highlights.appModule),
CodelabFile.Html('app')
.setCode(html)
.withHighlight(highlights.appHtml),
CodelabFile.TypeScriptFile('app.component').setCode(
require('!!raw-loader!./samples/basic/app.component.ts')
),
CodelabFile.TypeScriptFile('bootstrap')
.setCode(require('!!raw-loader!./samples/basic/main.ts'))
.makeBootstrappable(),
CodelabFile.Css('styles').setCode(
require('!!raw-loader!@angular/material/prebuilt-themes/indigo-pink.css')
),
CodelabFile.Css('extra').setCode('body {padding: 0; margin: 0;}')
]
};
}
@Component({
selector: 'codelab-slides-material',
templateUrl: './material.component.html',
styleUrls: ['./material.component.css'],
encapsulation: ViewEncapsulation.None
})
export class MaterialComponent implements AfterViewInit {
exercise: ExerciseConfigTemplate;
@ViewChild('themePlayground', { static: false }) themePlayground;
@ViewChild('translations', { static: false }) translations;
themes = {
indigo: require('!!raw-loader!@angular/material/prebuilt-themes/indigo-pink.css'),
deeppurple: require('!!raw-loader!@angular/material/prebuilt-themes/deeppurple-amber.css'),
pink: require('!!raw-loader!@angular/material/prebuilt-themes/pink-bluegrey.css'),
purple: require('!!raw-loader!@angular/material/prebuilt-themes/purple-green.css')
};
code = {
material: {
step1: {
code: {
'app.component.ts': require('!!raw-loader!./samples/basic/app.component.ts'),
'app.module.ts': require('!!raw-loader!./samples/step1/app.module.ts'),
'app.html': require('!!raw-loader!./samples/step1/app.html'),
'bootstrap.ts': require('!!raw-loader!./samples/basic/main.ts'),
'styles.css': this.themes.indigo
},
files: ['app.module.ts', 'app.html'],
highlights: {
'app.module.ts': 'MatToolbarModule,',
'app.html': //
}
},
step2: {
code: {
'app.component.ts': require('!!raw-loader!./samples/basic/app.component.ts'),
'app.module.ts': require('!!raw-loader!./samples/step2/app.module.ts'),
'app.html': require('!!raw-loader!./samples/step2/app.html'),
'bootstrap.ts': require('!!raw-loader!./samples/basic/main.ts'),
'styles.css': this.themes.indigo
},
files: ['app.module.ts', 'app.html'],
highlights: {
'app.module.ts': /MatCardModule,/,
'app.html': //
}
},
step3: {
code: {
'app.component.ts': require('!!raw-loader!./samples/basic/app.component.ts'),
'app.module.ts': require('!!raw-loader!./samples/step2/app.module.ts'),
'app.html': require('!!raw-loader!./samples/step3/app.html'),
'bootstrap.ts': require('!!raw-loader!./samples/basic/main.ts'),
'styles.css': this.themes.indigo
},
files: ['app.html'],
highlights: {
'app.html': //
}
},
step4: {
code: {
'app.component.ts': require('!!raw-loader!./samples/basic/app.component.ts'),
'app.module.ts': require('!!raw-loader!./samples/step4/app.module.ts'),
'app.html': require('!!raw-loader!./samples/step4/app.html'),
'bootstrap.ts': require('!!raw-loader!./samples/basic/main.ts'),
'styles.css': this.themes.indigo
},
files: ['app.module.ts', 'app.html'],
highlights: {
'app.module.ts': /MatButtonModule\n/,
'app.html': //
}
},
themes: {
code: {
'app.component.ts': require('!!raw-loader!./samples/basic/app.component.ts'),
'app.module.ts': require('!!raw-loader!./samples/step4/app.module.ts'),
'app.html': require('!!raw-loader!./samples/step4/app.html'),
'bootstrap.ts': require('!!raw-loader!./samples/basic/main.ts'),
'styles.css': this.themes.indigo
},
files: ['app.html', 'styles.css']
},
theme: matExercise(
`MatToolbarModule,\n MatCardModule,\n MatButtonModule`,
require('!!raw-loader!./samples/basic/app.4.html')
)
}
};
private theme = 'indigo';
private t: Record;
constructor(private exercises: Ng2TsExercises) {
this.exercise = exercises.getExercises(6, 0);
}
ngAfterViewInit() {
this.t = extractMessages(this.translations);
}
setTheme(theme) {
this.theme = theme;
this.code.material.themes.code['styles.css'] = this.themes[theme];
this.code.material.themes.code = { ...this.code.material.themes.code };
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/material.module.ts
================================================
import { MaterialComponent } from './material.component';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { CodeDemoModule } from '@codelab/code-demos';
import { Ng2TsExercises } from '../../../../../../../ng2ts/ng2ts';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild([...SlidesRoutes.get(MaterialComponent)]);
@NgModule({
imports: [
routes,
FeedbackModule,
CommonModule,
BrowserWindowModule,
MatButtonModule,
MatCardModule,
MatInputModule,
CodelabComponentsModule,
SlidesModule,
FormsModule,
CodeDemoModule
],
declarations: [MaterialComponent],
exports: [MaterialComponent],
providers: [Ng2TsExercises]
})
export class MaterialCodelabModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.1.html
================================================
Hi, I'm material toolbar
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.2.html
================================================
Hi, I'm material toolbar
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.3.html
================================================
Hi, I'm material toolbar
Shiba Inu
Dog Breed
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.4.html
================================================
Header
Shiba Inu
Dog Breed
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
LIKE
SHARE
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
templateUrl: './app.html'
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.html
================================================
Header
Shiba Inu
Dog Breed
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
LIKE SHARE
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatToolbarModule } from '@angular/material/toolbar';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
NoopAnimationsModule,
MatToolbarModule,
MatCardModule,
MatButtonModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/code.ts
================================================
// I'm ignored
export const hi = 'hi';
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/basic/main.ts
================================================
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { ResourceLoader } from '@angular/compiler';
import * as code from './code';
class MyResourceLoader extends ResourceLoader {
get(url: string): Promise {
const templateId = Object.keys(code).find(key =>
key.includes(url.replace(/[\/\.-]/gi, '_'))
);
const template = code[templateId];
if (!template) {
console.log(template);
// tslint:disable-next-line:no-debugger
debugger;
}
return Promise.resolve(template);
}
}
platformBrowserDynamic().bootstrapModule(AppModule, [
{
providers: [
{
provide: ResourceLoader,
useFactory: () => new MyResourceLoader(),
deps: []
}
]
}
]);
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step1/app.html
================================================
Hi, I'm material toolbar
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step1/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatToolbarModule } from '@angular/material/toolbar';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
NoopAnimationsModule,
MatToolbarModule,
MatCardModule,
MatButtonModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step2/app.html
================================================
Hi, I'm material toolbar
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step2/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatToolbarModule } from '@angular/material/toolbar';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
NoopAnimationsModule,
MatToolbarModule,
MatCardModule,
MatButtonModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step3/app.html
================================================
Hi, I'm material toolbar
Shiba Inu
Dog Breed
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step4/app.html
================================================
Header
Shiba Inu
Dog Breed
The Shiba Inu is the smallest of the six original and distinct spitz
breeds of dog from Japan. A small, agile dog that copes very well with
mountainous terrain, the Shiba Inu was originally bred for hunting.
LIKE
SHARE
================================================
FILE: apps/codelab/src/app/codelabs/angular/material/samples/step4/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatToolbarModule } from '@angular/material/toolbar';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AppComponent } from './app.component';
@NgModule({
imports: [
BrowserModule,
NoopAnimationsModule,
MatToolbarModule,
MatCardModule,
MatButtonModule
],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/pipes.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/pipes.component.html
================================================
Introduction
Every application starts out with what seems like a simple task: get
data, transform it, and show it to users. Getting data could be as
simple as creating a local variable or as complex as streaming data over
a WebSocket.
An Angular pipe is a function that transforms input values to
output values for display in a view. Let's see how it works.
How do pipes work?
A pipe takes in data as input and transforms it to a desired output.
Inside the interpolation expression, we use the pipe
( | ) operator to pass the App Component's 'dob' through the
the 'date' pipe on the right, transforming a long timestamp into a more
human-readable output. All pipes follow this signature.
Built-in Pipes
Angular comes with a variety of built-in pipes that can be used in any
template or binding expression. Some of these pipes are:
DatePipe
UpperCasePipe
LowerCasePipe
CurrencyPipe
PercentPipe
You can check out all the pipes
in the docs.
Chained Pipes
Translate Dali's birthday to a more human-friendly format. While you're at
it, make his birthday uppercase, too.
Hint: dob ... date ... uppercase
Pipes with arguments:
To add parameters to a pipe, append a colon ( : ) followed by the
parameters' values e.g. currency:'AUD'
Using custom pipes to filter data
Creating a pipe
A pipe is a class decorated with pipe metadata.
Creating a pipe
The pipe class implements the PipeTransform interface.
Creating a pipe
The transform method accepts an input value followed by optional
parameters and returns the transformed value.
Creating a pipe
The @Pipe decorator allows you to define the pipe name that you'll use
within template expressions.
Don't forget to import the @Pipe decorator from the core Angular library.
Well done! This is the end of the milestone!
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/pipes.component.ts
================================================
import { Component } from '@angular/core';
import { displayAngularComponent } from '../../../shared/helpers/helpers';
@Component({
selector: 'codelab-slides-pipes',
templateUrl: './pipes.component.html',
styleUrls: ['./pipes.component.css']
})
export class PipesComponent {
code = {
chainedPipesExample: {
template: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Salvador's Dali DOB
{{ dob }}
\`
})
export class AppComponent {
dob = new Date(1904, 4, 11);
}`)
},
workingPipes: {
template: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Salvador's Dali DOB
{{dob}}
{{ dob | date }}
\`
})
export class AppComponent {
dob = new Date(1904, 4, 11);
}`),
matches: {
pipeOperator: '|'
}
},
argumentPipes: {
template: `Your budget is {{budget | currency:'AUD'}}
Your truncated name is {{name | substring:1:4}}
`,
readonly: true,
path: 'argument.pipe.html'
},
filterPipes: {
template: `
`,
readonly: true,
path: 'filter.pipe.html'
},
creatingAPipe: {
template: `import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'substring'})
export class SubstringPipe implements PipeTransform {
transform(value: string, start: number, end: number): string {
return (value || '').slice(start, end);
}
}`,
matches: {
exportClass: /export.*/,
decorator: /@Pipe/,
pipeTransform: /PipeTransform/,
method: /transform[^]*?\)[^]/
},
readonly: true,
path: 'substring.pipe.ts',
type: 'typescript'
}
};
constructor() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/pipes.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { CodeDemoModule } from '@codelab/code-demos';
import { PipesComponent } from './pipes.component';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild(SlidesRoutes.get(PipesComponent));
@NgModule({
imports: [
routes,
CodeDemoModule,
BrowserWindowModule,
FeedbackModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
declarations: [PipesComponent],
exports: [PipesComponent]
})
export class PipesModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/samples/pipes/app.component.html
================================================
Wow, check out this terribly formatted date:
{{ birthday }}
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/samples/pipes/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
birthday = new Date('2012-01-19');
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/pipes/samples/pipes/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
// Useless app module to prevent angular from complaining
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/playground/angular-sample.ts
================================================
export const angularSampleCode = {
'app.component.ts': `import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: \`Edit me \`
})
export class AppComponent {}`,
'app.module.ts': `import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}`,
'main.ts': `import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
`,
'index.html': ' '
};
================================================
FILE: apps/codelab/src/app/codelabs/angular/playground/playground.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/playground/playground.component.html
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/playground/playground.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PlaygroundComponent } from './playground.component';
import { PlaygroundModule } from './playground.module';
import { ActivatedRoute, Router } from '@angular/router';
describe('PlaygroundComponent', () => {
let component: PlaygroundComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [PlaygroundModule],
providers: [
{
provide: ActivatedRoute,
useValue: { snapshot: { queryParams: { code: '' } } }
},
{
provide: Router,
useValue: {
navigate: jasmine.createSpy('navigate')
}
}
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PlaygroundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create!', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/playground/playground.component.ts
================================================
import { Component } from '@angular/core';
import { angularSampleCode } from './angular-sample';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'codelab-playground',
templateUrl: './playground.component.html',
styleUrls: ['./playground.component.css']
})
export class PlaygroundComponent {
code = angularSampleCode;
constructor(
private readonly activatedRoute: ActivatedRoute,
private readonly router: Router
) {
const code = activatedRoute.snapshot.queryParams.code;
if (code) {
try {
this.code = { ...angularSampleCode, ...JSON.parse(atob(code)) };
} catch (e) {
console.log('can not parse code', code);
}
}
}
handleUpdate(code: any) {
const encoded = btoa(JSON.stringify(code));
this.router.navigate([], {
relativeTo: this.activatedRoute,
queryParams: { code: encoded }
});
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/playground/playground.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PlaygroundComponent } from './playground.component';
import { RouterModule } from '@angular/router';
import { CodeDemoModule } from '@codelab/code-demos';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [PlaygroundComponent],
imports: [
RouterModule.forChild([{ path: '', component: PlaygroundComponent }]),
CodeDemoModule,
CommonModule,
FormsModule
]
})
export class PlaygroundModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/router.component.css
================================================
.router-preview {
display: flex;
}
.router-preview codelab-exercise-preview {
margin: 10px;
}
.router-preview ::ng-deep .url {
color: black !important;
background: #f8ff15 !important;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/router.component.html
================================================
app.module.ts: Using RouterModule.forRoot provide an empty array to the
module
app.module.ts: Add a route with an empty ('') path to display
SearchComponent
app.module.ts: Add a route with path of "upload" to display UploadComponent
app.html: Add router-outlet
Intro
Router is used to give URLs to different parts of your app.
Intro
It takes 3 steps to set up routing in Angular:
Configure the mapping and specify which component to display for which
URL
Create the menu with navigation links
Find a place to display selected component
Configuration
Routes are configured by defining an array of mapping between URL path and
a component
Configuration
Then we have to pass the config to our module.
Note that we're using RouterModule.forRoot which creates an Angular
Module out of our configuration.
router-outlet
Now we have to find a place where the router would display selected
component.
It can be marked by placing router-outlet tag
Exercise
In the next slide there is an exercise. We're going to add 2 routes:
Search and Upload , and a menu for switching between them.
Note: We have already created empty Upload component and SearchComponent
containing search logic.
Well done! This is the end of the milestone!
Check out
Angular Router Guide for
more fun and advanced techniques.
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/router.component.ts
================================================
import { CodelabFile } from '../../../shared/helpers/codelabFile';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import {
ExerciseConfigTemplate,
Ng2TsExercises
} from '../../../../../../../ng2ts/ng2ts';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
declare const require;
interface FileHighlights {
appModule?: RegExp | RegExp[];
appHtml?: RegExp | RegExp[];
}
@Component({
selector: 'codelab-slides-router',
templateUrl: './router.component.html',
styleUrls: ['./router.component.css']
})
export class RouterComponent implements AfterViewInit {
@ViewChild('translations', { static: false }) translations;
private t: Record;
exercise: ExerciseConfigTemplate;
code = {
files: {
'app.module.ts': require('!!raw-loader!./samples/simple-router/app.module.ts'),
'app.component.html': require('!!raw-loader!./samples/simple-router/app.component.html'),
'app.component.ts': require('!!raw-loader!./samples/simple-router/app.component.ts'),
'components/kitten.ts': require('!!raw-loader!./samples/simple-router/components/kitten.ts'),
'components/puppy.ts': require('!!raw-loader!./samples/simple-router/components/puppy.ts'),
'bootstrap.ts': require('!!raw-loader!./samples/simple-router/main.ts'),
'index.html': require('!!raw-loader!./samples/simple-router/index.html'),
'components/.html': require('!!raw-loader!./samples/simple-router/index.html')
},
config: {
files: ['app.module.ts'],
highlights: { 'app.module.ts': /const routes[\s\S]*?];[\s\S]/ }
},
configPass: {
files: ['app.module.ts'],
highlights: { 'app.module.ts': /RouterModule.forRoot\(routes\)/ }
},
routerOutlet: {
files: ['app.component.html'],
highlights: { 'app.component.html': /<\/router-outlet>/ }
},
menu: {
files: ['app.component.html'],
highlights: { 'app.component.html': // }
}
};
constructor(private exercises: Ng2TsExercises) {
this.exercise = exercises.getExercises(5, 0);
}
ngAfterViewInit() {
this.t = extractMessages(this.translations);
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/router.module.ts
================================================
import { RouterComponent } from './router.component';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { CodeDemoModule } from '@codelab/code-demos';
import { Ng2TsExercises } from '../../../../../../../ng2ts/ng2ts';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild([...SlidesRoutes.get(RouterComponent)]);
@NgModule({
imports: [
routes,
FeedbackModule,
CommonModule,
BrowserWindowModule,
CodelabComponentsModule,
SlidesModule,
FormsModule,
CodeDemoModule
],
declarations: [RouterComponent],
providers: [Ng2TsExercises],
exports: [RouterComponent]
})
export class RouterCodelabModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/app.component.html
================================================
Puppies And Kittens
Puppies | Kittens
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
// tslint:disable-next-line
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { RouterModule, Routes } from '@angular/router';
import { PuppyComponent } from './components/puppy';
import { KittenComponent } from './components/kitten';
import { BrowserModule } from '@angular/platform-browser';
const routes: Routes = [
{ path: '', component: PuppyComponent },
{ path: 'kittens', component: KittenComponent }
];
@NgModule({
declarations: [AppComponent, PuppyComponent, KittenComponent],
imports: [BrowserModule, RouterModule.forRoot(routes)],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/code.ts
================================================
// I'm ignored
export const hi = 'hi';
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/components/kitten.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-i-am-not-very-important',
template: 'Kittens '
})
export class KittenComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/components/puppy.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-i-will-be-ignored',
template: 'Puppies! '
})
export class PuppyComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/index.html
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/router/samples/simple-router/main.ts
================================================
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
import { ResourceLoader } from '@angular/compiler';
import * as code from './code';
class MyResourceLoader extends ResourceLoader {
get(url: string): Promise {
const templateId = Object.keys(code).find(key =>
key.includes(url.replace(/[\/\.-]/gi, '_'))
);
const template = code[templateId];
if (!template) {
console.log(template);
// tslint:disable-next-line:no-debugger
debugger;
}
return Promise.resolve(template);
}
}
platformBrowserDynamic().bootstrapModule(AppModule, {
providers: [
{
provide: ResourceLoader,
useFactory: () => new MyResourceLoader(),
deps: []
}
]
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/bsod.css
================================================
@import url(https://fonts.googleapis.com/css?family=Press+Start+2p);
.container {
display: flex;
font-family: 'Press Start 2P', cursive;
align-items: center;
width: 100%;
height: 100%;
}
.msg {
padding-left: 100px;
padding-right: 100px;
}
p {
text-align: left;
}
.continue {
text-align: center;
}
.highlight {
color: rgb(1, 2, 172);
background-color: rgb(172, 173, 168);
padding: 3px;
text-align: center;
width: 150px;
}
.blink {
animation: blink 1s steps(2, start) infinite;
}
@keyframes blink {
to {
visibility: hidden;
}
}
#bsod,
#bsod-2,
#bsod-beautiful {
color: white;
background: #084fdd;
display: inline-block;
height: 100vh;
}
#bsod-2 .alfred-upset {
position: absolute;
left: 0;
top: 0;
width: 263px;
height: 420px;
background: url(./pics/Alfred_Sisley_photo_full.jpg);
}
#bsod-beautiful {
background: url(./pics/bebbf81f5402fbbec1cff6eb687aa90a.jpg) no-repeat center
center fixed;
background-size: 100%;
color: black;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/mat-tab-nav-bar/alert.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: '' + 'app-alert',
template: `
Hi ALert
`
})
export class AlertComponent {
constructor() {
alert('Hello');
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/mat-tab-nav-bar/app.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
template: `
{{ tab.label }}
`
})
export class AppComponent {
tabs = [
{
link: '',
label: 'Tab 1'
},
{
link: 'danger',
label: 'Danger'
}
];
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/mat-tab-nav-bar/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { AlertComponent } from './alert.component';
import { TabComponent } from './tab.component';
import { RouterModule } from '@angular/router';
import { APP_BASE_HREF } from '@angular/common';
import { MatTabsModule } from '@angular/material/tabs';
const routes = [
{ path: '', component: TabComponent },
{ path: 'danger', component: AlertComponent }
];
@NgModule({
imports: [BrowserModule, MatTabsModule, RouterModule.forRoot(routes)],
declarations: [AppComponent, AlertComponent, TabComponent],
bootstrap: [AppComponent],
providers: [{ provide: APP_BASE_HREF, useValue: '/assets/runner/' }]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/mat-tab-nav-bar/tab.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
template: `
`
})
export class TabComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/alert.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: '' + 'app-alert',
template: `
This is AlertComponent!
`
})
export class AlertComponent {
constructor() {
alert('Hello');
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/app.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
templateUrl: './app.html'
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/app.html
================================================
Alfred Sisley
TBD
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { MatTabsModule } from '@angular/material/tabs';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AlertComponent } from './alert.component';
import { BreakMyComputerComponent } from './break-my-computer.component';
import { TaetLedComponent } from './taet-led.component';
@NgModule({
imports: [BrowserModule, MatTabsModule, NoopAnimationsModule],
declarations: [
AppComponent,
AlertComponent,
BreakMyComputerComponent,
TaetLedComponent
],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/app.solved.html
================================================
Alfred Sisley
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/break-my-computer.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'break-my-computer',
template: `
I'll break your computer
`
})
export class BreakMyComputerComponent {
constructor() {
alert('Congratulations! Your computer has been broken successfully!');
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/style.css
================================================
body,
html {
width: 100%;
height: 100%;
}
body,
h1,
p,
div {
font-family: 'Helvetica Neue', sans-serif;
font-weight: 300;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs/taet-led.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'play-terrible-russian-pop-music',
template: `
VIDEO
`
})
export class TaetLedComponent {
constructor() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/alert.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: '' + 'app-alert',
template: `
Hi ALert
`
})
export class AlertComponent {
constructor() {
alert('Hello');
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/app.component.ts
================================================
import { Component } from '@angular/core';
/* tslint:disable */
@Component({
selector: 'my-app',
templateUrl: './app.html'
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/app.html
================================================
Alfred Sisley
Content 2
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { MatTabsModule } from '@angular/material/tabs';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { AlertComponent } from './alert.component';
import { HideMeDirective } from './hideme.directive';
@NgModule({
imports: [BrowserModule, MatTabsModule, NoopAnimationsModule],
declarations: [AppComponent, AlertComponent, HideMeDirective],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/app.solved.html
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/hideme.directive.solved.ts
================================================
import {
AfterViewInit,
Directive,
TemplateRef,
ViewContainerRef
} from '@angular/core';
import { MatTabGroup, MatTab } from '@angular/material/tabs';
/* tslint:disable */
@Directive({ selector: '[matHideMe]' })
export class HideMeDirective implements AfterViewInit {
constructor(
private parentTab: MatTab,
private tabs: MatTabGroup,
private viewContainer: ViewContainerRef,
private templateRef: TemplateRef
) {
(tabs as any).selectChange.subscribe(({ tab }: { tab: MatTab }) => {
this.toggleContentDisplay(tab === parentTab);
});
}
toggleContentDisplay(isDisplayed: boolean) {
if (isDisplayed) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
}
ngAfterViewInit() {
this.toggleContentDisplay(
this.parentTab.position === this.tabs.selectedIndex
);
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/hideme.directive.ts
================================================
import { Directive } from '@angular/core';
/* tslint:disable */
@Directive({ selector: '[matHideMe]' })
export class HideMeDirective {
constructor() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/material-tabs-structural-directive/ignored.module.ts
================================================
import { NgModule } from '@angular/core';
import { HideMeDirective } from './hideme.directive.solved';
// This is needed because angular cli wants the directive to be in a module
// https://github.com/angular/angular/issues/13590
@NgModule({
declarations: [HideMeDirective]
})
export class IgnoredModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/micro-syntax/code.ts
================================================
// Needed for type checking,
export const app_html = '';
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/micro-syntax/ms.spec.ts
================================================
import { parseTemplate } from './ms';
describe('micro-syntax', () => {
it('parses ngIF', () => {
expect(parseTemplate('{{hero.name}}
'))
.toBe(`
{{hero.name}}
`);
});
it('parses ngIf multi line', () => {
expect(
parseTemplate(`
{{hero.name}}
`)
).toBe(`
{{hero.name}}
`);
});
xit('parses ngFor', () => {
expect(
parseTemplate(`
{{hero.name}}
`)
).toBe(`
{{hero.name}}
`);
});
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/micro-syntax/ms.ts
================================================
import * as code from './code';
export function parseTemplate(template: string) {
const x = template.match(
/<([^ ]+)\s+\*(\w+)\s*="([^"]+)"\s*>([\s\S]*)<\/\w+>/
);
if (x) {
const [_, tag, directive, value, contents] = x;
return `
<${tag}>${contents}${tag}>
`;
}
}
const pre = document.createElement('pre');
document.body.appendChild(pre);
pre.innerText = parseTemplate(code.app_html);
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/structural-directives/microsyntax.html
================================================
*:prefix="( :let | :expression ) (';' | ',')? ( :let | :as | :keyExp )*"
:prefix: HTML attribute key. :key: HTML attribute key. :local: local variable
name used in the template. :export: value exported by the directive under a
given name. :experession: standard Angular expression :keyExp = :key ":"?
:expression ("as" :local)? ";"? :let = "let" :local "=" :export ";"? :as =
:export "as" :local ";"?
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/structural-directives/ng-for-after.html
================================================
({{i}}) {{hero.name}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/structural-directives/ng-for-before.html
================================================
({{i}}) {{hero.name}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/structural-directives/ng-if-after.html
================================================
{{hero.name}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/samples/structural-directives/ng-if-before.html
================================================
{{hero.name}}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/structural-directives.component.css
================================================
#intro .bg {
background: #fff;
opacity: 0.5;
padding: 2vw 5vw;
margin: 5vw 0;
}
#intro h1 {
font-size: 10vw;
}
#intro h2 {
font-size: 6vw;
}
:host /deep/ .slide {
background: transparent;
}
#intro {
background: url(./pics/bridge-at-villeneuve-la-garenne-1872.jpg) no-repeat;
display: inline-block;
height: 100vh;
}
#we-need-tabs {
background: url(./pics/alfred-sisley-9485226-1-402.jpg) no-repeat black;
display: inline-block;
height: 100vh;
}
#we-need-tabs h1 {
color: #ffffee;
font-size: 8vw;
background: #000;
opacity: 0.5;
}
:host /deep/ .runner,
:host /deep/ .runner iframe {
display: block;
width: 100%;
height: 100%;
}
/* Specificity */
:host /deep/ .side.side.side {
display: block;
}
.font-size:hover {
opacity: 1;
}
.font-size {
display: block;
opacity: 0.1;
background-color: #eeeeee;
border-radius: 50%;
font-size: 30px;
width: 30px;
height: 30px;
cursor: pointer;
text-align: center;
}
.twitter {
position: absolute;
left: 20px;
bottom: 20px;
color: #400;
height: 50px;
z-index: 100;
font-size: 50px;
font-family: Monaco, 'Lucida Console', monospace;
opacity: 1;
width: auto;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/structural-directives.component.html
================================================
Structural directives
By @kirjs
We need tabs
Windows
A fatal exception 0E has occured at 028:C00068F8 in VxD VMM(01)
000059F8. The current application will be terminated.
* Press any key to terminate the application.
* Press CTRL+ALT+DEL to restart your computer. You will lose any
unsaved information in all aplications.
Press any key to continue _
Windows
A fatal exception 0E has occured at 028:C00068F8 in VxD VMM(01)
000059F8. The current application will be terminated.
* Press any key to terminate the application.
* Press CTRL+ALT+DEL to restart your computer. You will lose any
unsaved information in all aplications.
Press any key to continue _
Windows
A fatal exception 0E has occured at 028:C00068F8 in VxD VMM(01)
000059F8. The current application will be terminated.
* Press any key to terminate the application.
* Press CTRL+ALT+DEL to restart your computer. You will lose any
unsaved information in all aplications.
Press any key to continue _
Structural directives ngIf
Desugars into
Structural directives ngFor
Desugars into
Russian pop music
@kirjs
VIDEO
VIDEO
VIDEO
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/structural-directives.component.ts
================================================
import { Component } from '@angular/core';
import {
bootstrap,
builder,
exercise,
html,
stylesheet
} from '../../../shared/helpers/helpers';
declare const require;
@Component({
selector: 'codelab-slides-structural-directives',
templateUrl: './structural-directives.component.html',
styleUrls: ['./structural-directives.component.css', './bsod.css']
})
export class StructuralDirectivesComponent {
fontSize = 18;
code = {
materialTabs: {
files: [
html(
'app',
require('!!raw-loader!./samples/material-tabs/app.html'),
require('!!raw-loader!./samples/material-tabs/app.solved.html')
),
exercise(
'app.component',
require('!!raw-loader!./samples/material-tabs/app.component.ts')
),
exercise(
'alert.component',
require('!!raw-loader!./samples/material-tabs/alert.component.ts')
),
exercise(
'taet-led.component',
require('!!raw-loader!./samples/material-tabs/taet-led.component.ts')
),
exercise(
'app.module',
require('!!raw-loader!./samples/material-tabs/app.module.ts')
),
exercise(
'break-my-computer.component',
require('!!raw-loader!./samples/material-tabs/break-my-computer.component.ts')
),
stylesheet(require('!!raw-loader!./samples/material-tabs/style.css')),
bootstrap('main', builder.bootstrap())
]
},
materialTabsStructuralDirective: [
html(
'app',
require('!!raw-loader!./samples/material-tabs-structural-directive/app.html'),
require('!!raw-loader!./samples/material-tabs-structural-directive/app.solved.html')
),
exercise(
'app.component',
require('!!raw-loader!./samples/material-tabs-structural-directive/app.component.ts')
),
exercise(
'hideme.directive',
require('!!raw-loader!./samples/material-tabs-structural-directive/hideme.directive.ts'),
require('!!raw-loader!./samples/material-tabs-structural-directive/hideme.directive.solved.ts')
),
exercise(
'alert.component',
require('!!raw-loader!./samples/material-tabs-structural-directive/alert.component.ts')
),
exercise(
'app.module',
require('!!raw-loader!./samples/material-tabs-structural-directive/app.module.ts')
),
stylesheet(require('!!raw-loader!./samples/material-tabs/style.css')),
bootstrap('main', builder.bootstrap())
],
microSyntax: [
html('app', `
`),
bootstrap('main', require('!!raw-loader!./samples/micro-syntax/ms.ts'))
],
mdTabNavBar: [
exercise(
'app.component',
require('!!raw-loader!./samples/mat-tab-nav-bar/app.component.ts')
),
exercise(
'alert.component',
require('!!raw-loader!./samples/mat-tab-nav-bar/alert.component.ts')
),
exercise(
'tab.component',
require('!!raw-loader!./samples/mat-tab-nav-bar/tab.component.ts')
),
stylesheet(require('!!raw-loader!./samples/material-tabs/style.css')),
exercise(
'app.module',
require('!!raw-loader!./samples/mat-tab-nav-bar/app.module.ts')
),
bootstrap('main', builder.bootstrap())
],
structuralDirectives: {
ngIfBefore: require('!!raw-loader!./samples/structural-directives/ng-if-before.html'),
ngIfAfter: require('!!raw-loader!./samples/structural-directives/ng-if-after.html'),
ngForBefore: require('!!raw-loader!./samples/structural-directives/ng-for-before.html'),
ngForAfter: require('!!raw-loader!./samples/structural-directives/ng-for-after.html'),
microSyntax: require('!!raw-loader!./samples/structural-directives/microsyntax.html')
}
};
constructor() {}
updateFontSize(diff) {
this.fontSize += diff;
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/structural-directives/structural-directives.module.ts
================================================
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { CodeDemoModule } from '@codelab/code-demos';
import { BrowserWindowModule } from '@codelab/browser';
import { StructuralDirectivesComponent } from './structural-directives.component';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild(
SlidesRoutes.get(StructuralDirectivesComponent)
);
@NgModule({
imports: [
routes,
CodeDemoModule,
BrowserWindowModule,
FeedbackModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
declarations: [StructuralDirectivesComponent],
exports: [StructuralDirectivesComponent]
})
export class StructuralDirectivesModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/data-binding-extra/app.component.html
================================================
Here we bind value of the input to the name property
A shortcut applying (or not) class name based on the value of isSpecial
Wow, I'm special!
Shortcut for binding styles
special button
It also works with custom components
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/data-binding-extra/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
readonly name = 'Camille Pissarro';
isSpecial = true;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/data-binding-extra/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BirthdayCardComponent } from './number-praiser';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, BirthdayCardComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/data-binding-extra/index.html
================================================
Loading...
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/data-binding-extra/number-praiser.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: 'number-praiser',
template: `
🎈 {{ number }} 🎈 What an amazing number!!! 🎖
`
})
export class BirthdayCardComponent {
@Input() number = 0;
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/event-binding/app.component.html
================================================
Message: {{ message }}
Update version
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/event-binding/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
message = 'no message';
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/event-binding/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
// Useless app module to prevent angular from complaining
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/event-binding-shortcuts/app.component.html
================================================
Message: {{ message }}
Update message
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/reference-binding/app.component.html
================================================
Message: {{ message }}
Update message
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/reference-binding/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html'
})
export class AppComponent {
message = 'No message';
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/samples/reference-binding/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
// Useless app module to prevent angular from complaining
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/templates.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/templates.component.html
================================================
This is valid HTML syntax.
It works on attribute syntax.
It allows to conditionally bind a class
Or style properties
And works with custom components!
When user clicks the button, it calls the "saveUser" function on the
component instance and passes the underlying event.
You can also create events for custom components. Here we have a depleted
event, and it's going to call the "soundAlarm" function on the component
instance when it fires.
There are also shortcut event bindings! The submit function on the component
instance will be called when the user presses control and enter (this is an
Angular feature).
userName has a reference to the input element
Try changing to true!
Need to repeat puppies here
app.component.ts: Add a 'videos' property, set the value as empty array.
app.component.ts: Inside of the 'search' method assign FAKE_VIDEOS, to the
component 'videos' property.
app.component.ts: Add a 'search' method on the component, that takes a
'searchString' parameter.
app.html: Add a click handler to the button, call 'search' method and pass
the input value (Actual search functionality will be implemented in the next
exercise)
app.html: Add a message saying 'no videos' which is displayed only when the
videos array is empty
#Bonus app.component.ts: Right now it takes pressing a search button to
display the videos. Instead display all videos by default.
app.component.ts: Inside of the 'search' method filter FAKE_VIDEOS and only
return videos with the title containing searchString. (hint: use .includes
or .indexOf string methods)
app.html: Also display a thumbnail
app.html: Iterate over the videos using '*ngFor', and display a title for
each
app.html: Add a button tag with a text 'search'
app.html: Add an input tag with a 'placeholder' attribute set to 'video'
Intro
Angular has a very expressive template system, which takes HTML as a base,
and extends it with custom elements
Interpolation
Double curlies include the appropriate component property value
Backticks ` ` , are magic quotes that allow multi-line strings and
text interpolation.
Interpolation
Simple expressions are also allowed, you can run a component method (like
fullName() below), or calculate 323213+34234
Exercise
In the next slide you'll edit a component template to create a simple
header and search form. The result will look like this:
Properties
String interpolation {{ curlies }} can also be used to pass a value
to a child element's attribute
Property Binding
Better option is to use property binding [attribute] = property
You can use arbitrary expressions in the binding.
Event binding: (event)
For handling user actions we can use event bindings. To do that we wrap
the event name in parentheses, and pass an expression performing required
action:
While parentheses are used for event binding: (event) , "on-" can
also be used, e.g. on-click is the same as (click) .
Event binding shortcuts
When we need to access an HTML element or an Angular component from the
template, we can mark it with #name , and it becomes available as
name everywhere in the template:
We'll learn a better way to work with inputs in Forms milestone.
Event binding shortcuts
Angular also provides a shortcut for handling keyboard shortcuts. Try
updating the message by pressing Control + Enter in the input.
Conditional Display (*ngIf)
This conditional expression will add or remove an element from the DOM if
it evaluates as a truthy
Exercise 2
In the next slide you'll add a click handler to the search button, and
display a message for the case where no videos were found. The result will
look like this:
Repeating elements
Let's say you have an array of puppies, and want to display all of them on
the page. Angular has a special syntax for that called *ngFor ,
let's see how it works on the next slide
Repeating elements (*ngFor)
Here *ngFor repeats HTML element it's attached to (li in this case)
for every single puppy in the puppies array
HTML attributes in Angular are case sensitive:
*ngfor won't work, *ngFor will
Exercise 3
In the next slide you'll finally display the videos! The result will
look like this:
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/templates.component.ts
================================================
import { Component, OnInit, ViewChild } from '@angular/core';
import { ng2tsConfig } from '../../../../../../../ng2ts/ng2ts';
import {
displayAngularComponent,
displayAngularComponentWithHtml
} from '../../../shared/helpers/helpers';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
declare const require;
const baseCode = 'TODO';
@Component({
selector: 'codelab-slides-templates',
templateUrl: './templates.component.html',
styleUrls: ['./templates.component.css']
})
export class TemplatesComponent implements OnInit {
t: { [key: string]: string };
exercises = [
ng2tsConfig.milestones[2].exercises[1],
ng2tsConfig.milestones[2].exercises[2],
ng2tsConfig.milestones[2].exercises[3]
];
curlies = '{{ property }}';
// TODO(kirjs): we can't access tanslation in OnInit hook iwht static set to false
// need to consider changing how we set code
@ViewChild('translations', { static: true }) translation;
code: any = {};
constructor() {}
ngOnInit() {
this.t = extractMessages(this.translation);
this.code = {
template: {
intro: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: 'Hello World! '
})
export class AppComponent {
}`),
matches: {
curlies: { 'app.component.ts': [/{{.*}}/, /firstName = .*/] },
curliesFullName: { 'app.component.ts': [/{{.*}}/, /fullName\(\){/] },
curliesAttribute: { 'app.component.ts': [/"{{.*}}"/, /avatar = .*/] },
template: { 'app.component.ts': /.*<\/h1>/ },
squares: { 'app.component.ts': /\[.*]/ }
},
interpolation: displayAngularComponent(
`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`
Hello {{firstName}}!
\`
})
export class AppComponent {
firstName = 'Pierre-Auguste';
lastName = 'Renoir';
}`,
''
),
interpolationMethod: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Hello {{fullName()}}! \`
})
export class AppComponent {
firstName = 'Pierre-Auguste';
lastName = 'Renoir';
fullName(){
return this.firstName + " " + this.lastName
}
}`),
dataBindingPre: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Hello {{fullName()}}!
\`
})
export class AppComponent {
firstName = 'Pierre-Auguste';
lastName = 'Renoir';
avatar = 'assets/images/renoir.jpg';
fullName(){ return this.firstName + " " + this.lastName }
}`),
dataBinding: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Hello {{fullName()}}!
\`
})
export class AppComponent {
firstName = 'Pierre-Auguste';
lastName = 'Renoir';
avatar = 'assets/images/renoir.jpg';
fullName(){ return this.firstName + " " + this.lastName }
}`),
dataBindingExtra: {
code: {
'app.component.html': require('!!raw-loader!./samples/data-binding-extra/app.component.html'),
'app.component.ts': require('!!raw-loader!./samples/data-binding-extra/app.component.ts'),
'bootstrap.ts': require('!!raw-loader!./../../../shared/angular-code/bootstrap.ts'),
'app.module.ts': require('!!raw-loader!./samples/data-binding-extra/app.module.ts'),
'number-praiser.ts': require('!!raw-loader!./samples/data-binding-extra/number-praiser.ts'),
'index.html': require('!!raw-loader!./samples/data-binding-extra/index.html')
},
files: ['app.component.html', 'app.component.ts']
}
},
ngIfDirective: {
template: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Hello {{firstName}}!
\`
})
export class AppComponent {
firstName = 'Pierre-Auguste';
avatar = 'assets/images/renoir.jpg';
onDisplay(){ return false } // ${this.t.tryChangingToTrue}
}`),
matches: {
ngIf: { 'app.component.ts': /\*ngIf/ }
}
},
ngForDirectivePre: {
template: displayAngularComponent(`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Puppies names:
???
\`
})
export class AppComponent {
puppies = ['Schumann', 'Mendelssohn', 'Bach'];
}`),
matches: { 'app.component.ts': ['???', /puppies.*;/] }
},
ngForDirective: {
template: displayAngularComponent(
`import {Component} from '@angular/core';
@Component({
selector: 'my-app',
template: \`Puppies names:
\`
})
export class AppComponent {
puppies = ['Schumann', 'Mendelssohn', 'Bach'];
}`,
`
import {AppComponent} from './app.component';
describe('AppComponent', ()=>{
it('Add one more puppy to the list', ()=>{
const app = new AppComponent();
chai.expect(app.puppies.length).equals(4);
})
})
`
),
matches: {
ngFor: { 'app.component.ts': '*ngFor' }
}
},
templateInterpolation: `
Profile for {{person.name}}
Profile for {{person.getBiography()}}
`,
templateInterpolationMatch: /{{person.name}}/,
templateInterpolationExercise: displayAngularComponentWithHtml(
baseCode,
`Hello, {{user.firstName}} `
),
templateInterpolationExerciseMatch: /user.firstName/,
bindingPropMatch: /person.photoUrl/,
bindingPropExercise: displayAngularComponentWithHtml(
baseCode,
` `
),
bindingPropExerciseMatch: /user.pic/,
eventBinding: {
code: {
'app.component.html': require('!!raw-loader!./samples/event-binding/app.component.html'),
'app.component.ts': require('!!raw-loader!./samples/event-binding/app.component.ts'),
'bootstrap.ts': require('!!raw-loader!./../../../shared/angular-code/bootstrap.ts'),
'app.module.ts': require('!!raw-loader!./../../../shared/angular-code/app.module.ts'),
'index.html': require('!!raw-loader!./../../../shared/angular-code/index.html')
},
files: ['app.component.html'],
highlights: { 'app.component.html': '(click)' }
},
referenceBinding: {
code: {
'app.component.html': require('!!raw-loader!./samples/reference-binding/app.component.html'),
'app.component.ts': require('!!raw-loader!./samples/reference-binding/app.component.ts'),
'bootstrap.ts': require('!!raw-loader!./../../../shared/angular-code/bootstrap.ts'),
'app.module.ts': require('!!raw-loader!./../../../shared/angular-code/app.module.ts'),
'index.html': require('!!raw-loader!./../../../shared/angular-code/index.html')
},
files: ['app.component.html'],
highlights: { 'app.component.html': ['#input', 'input.value'] }
},
eventBindingShortcuts: {
code: {
'app.component.html': require('!!raw-loader!./samples/event-binding-shortcuts/app.component.html'),
'app.component.ts': require('!!raw-loader!./samples/reference-binding/app.component.ts'),
'bootstrap.ts': require('!!raw-loader!./../../../shared/angular-code/bootstrap.ts'),
'app.module.ts': require('!!raw-loader!./../../../shared/angular-code/app.module.ts'),
'index.html': require('!!raw-loader!./../../../shared/angular-code/index.html')
},
files: ['app.component.html'],
highlights: { 'app.component.html': '(keydown.control.enter)' }
},
eventBindingExercise: displayAngularComponentWithHtml(
baseCode,
``
),
conditionalDisplay: `
`,
conditionalDisplayMatch: /ngIf/,
conditionalDisplayExercise: displayAngularComponentWithHtml(
baseCode,
``
),
conditionalDisplayFor: ``,
conditionalDisplayForMatch: /ngFor/,
conditionalDisplayForExercise: displayAngularComponentWithHtml(
baseCode,
``
)
};
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/templates/templates.module.ts
================================================
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { CodeDemoModule } from '@codelab/code-demos';
import { TemplatesComponent } from './templates.component';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild([...SlidesRoutes.get(TemplatesComponent)]);
@NgModule({
imports: [
routes,
CodeDemoModule,
FeedbackModule,
CodelabComponentsModule,
SlidesModule,
FormsModule
],
declarations: [TemplatesComponent],
exports: [TemplatesComponent]
})
export class TemplatesModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/code/app.ts
================================================
export const value = { value: 4 };
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/code/code.ts
================================================
export { ts } from '../../../../../../../../../ng2ts/code';
export const app_ts_AST = {};
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/code/mini-exercise-test.ts
================================================
import { value } from './app';
import { app_ts_AST, ts } from './code';
declare const it, describe;
function getFunctionNode(code) {
let functionNode;
/**
* Fancy: Require the actual source code, and search in it.
*/
function findFunctionNode(node) {
if (
node.kind === ts.SyntaxKind.FunctionDeclaration &&
node.name.text === 'add'
) {
functionNode = node;
}
ts.forEachChild(node, findFunctionNode);
}
findFunctionNode(code);
return functionNode;
}
describe('value', () => {
it(`@@specifyTheTypeForB`, () => {
const func = getFunctionNode(app_ts_AST);
chai.assert(
func.parameters[1].type &&
func.parameters[1].type.kind === ts.SyntaxKind.NumberKeyword,
'Test failed: b is not a number'
);
});
it(`@@typescriptHighlightsErrorFix224`, () => {
chai.expect(value.value).equals(4);
});
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html
================================================
ES7
Decorators
Types
TypeScript
Classes
Modules
More...
ES6
ES5
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TypeScriptSvgComponent } from './typescript-svg.component';
describe('TypeScriptSvgComponent', () => {
let component: TypeScriptSvgComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TypeScriptSvgComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TypeScriptSvgComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'codelab-typescript-svg',
templateUrl: './typescript-svg.component.html'
})
export class TypeScriptSvgComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/typescript.component.css
================================================
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/typescript.component.html
================================================
Or use shorthand function notation.
Error: this is clearly not a puppy
This is a number
Or use shorthand function notation.
(Also called arrow function)
Actually TypeScript can infer number here;
TypeScript can infer it's a string.
Can't add number and boolean
Can't slice a number
But can slice a string!
Works!
Type[] does the same thing.
This is a method.
This a common russian dog name.
That's how russian dogs talk.
Now we can instantiate (create) it
And use its methods
Later we'll have code here
Let's create more puppies
Var is still allowed but not recommended.
Let should be used instead of var.
Unlike var let is unavailable outside of this if.
Const is like let, but if you try to change it, TS will give you an error.
okay, definitely a boolean
Create a class called 'Codelab'
Export the class
Add a constructor
Make constructor take a parameter 'guests'
Specify the type for the guests parameter (hint: it's an array of a type
Guest)
Make the parameter public (note that now you can access it anywhere in the
class using this.guests)
Create new method 'getGuestsComing'
"b" in the code below is highlighted, because TypeScript is missing the
type. Specify the type for b.
With this information TypeScript can highlight the error. Fix it, make 2 + 2
= 4 again!
Modify getGuestsComing to filter the guests array return an array of guests
with the 'coming' property set to true.
Why TypeScript
JavaScript is a great language, but there's space for improvement:
JS is not type safe which makes it harder to develop large scale
applications
New features of the latest versions of JS standards (ES2018, ES2019) are
not supported well across all the browsers
ES stands for
ECMAScript , which is the name of the JavaScript language specification (standard)
TypeScript
This is why TypeScript has been created. Since TypeScript can be
compiled to JavaScript, it can be used in any modern browser.
TypeScript extends the latest version of JavaScript
TypeScript adds new features from the next version of JavaScript
On top of it, TypeScript adds an optional type system and decorators
Decorator looks like @twitter_handles, we'll learn more about them later
Type System
Below we have an add function, and we're adding 2 and 2. What could
go wrong?
Turns out it's possible to pass a string to this function and we get
22 instead of 4 . Let's see how TypeScript can help address
this issue on the next slide
Type System
TypeScript uses ": " to specify the type information (e.g.
n: number ). Both a and b should be numbers. We
specified the type for a , now it's your turn!
The code above is editable!
Primitives (strings, numbers, etc...)
Below are more types we can use
Interfaces
TypeScript Interfaces allow to specify properties and methods for an
object.
Here, realPuppy is an implementation of the Puppy Interface.
Arrays
Array types are defined as Array{{ '<' }}Type{{ '>' }} or
Type[]
Here, each element in the betterCats array is an instance of the
Cat Interface.
Classes
TypeScript has classes , and Angular uses them heavily.
They are similar to classes in other languages, and are used to group
methods and properties together
Constructor
There's a special method on the class called constructor . It's run
when the class is instantiated and allows the class to take parameters
Access Modifiers
Constructor parameters marked as public (or private, or protected),
become class properties accessible as this.ParameterName within the
class
private or protected properties are not visible outside of the class.
Export
By the way, did you notice the export keyword before class? It is
used to share information between files. In the next slide, we'll show you
how to import and use this class in a different file
Import
Now we can use the Puppy class in the other file
import and export keywords are not just for classes. They
work with variables, functions and other things!
Filter (One last thing)
"filter " is an Array method that allows you to generate a new array
keeping only values that satisfy the condition
More
TypeScript supports lots of other cool features such as:
We won't cover them in detail, check out the
TypeScript
website!
Exercise
In the next slide we have a TypeScript exercise
Your task is to build a TypeScript class called Codelab which will take a
list of guests, and will have a method to output only the ones who are
coming.
The result will be as follows:
Milestone Completed
Now you should know enough TypeScript to start learning
Angular ! Read more about TypeScript on
TypeScript web site
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript/typescript.component.ts
================================================
import { Component, OnInit, ViewChild } from '@angular/core';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
import { ng2tsConfig } from '../../../../../../../../ng2ts/ng2ts';
import {
javaScriptWithConsoleLog,
typeScriptWithConsoleLog
} from '../../../../shared/helpers/helpers';
declare const require;
@Component({
selector: 'codelab-slides-typescript',
templateUrl: './typescript.component.html',
styleUrls: ['./typescript.component.css']
})
export class TypeScriptComponent implements OnInit {
t: { [key: string]: string };
// need to consider changing how we set code
@ViewChild('translations', { static: true }) translation;
// TODO(kirjs): we can't access tanslation in OnInit hook iwht static set to false
private exercises = [ng2tsConfig.milestones[0].exercises[1]];
private code: any = {};
ngOnInit(): void {
this.t = extractMessages(this.translation);
this.code = {
filter: typeScriptWithConsoleLog(`const numbers = [12,23,62,34,19,40,4,9];
console.log(numbers.filter(function(n: number){
return n > 30;
}));
// ${this.t.useShorthandNotation}
// ${this.t.calledArrowFunction}
console.log(
numbers.filter(n => n > 30)
);`),
moreTypes: {
codeInterfaces: `interface Puppy {
name: string;
age: number;
};
const realPuppy: Puppy = {
name: 'Pikachu',
age: 1
};
const notRealPuppy: Puppy = {
says: 'meow' // ${this.t.errorNotAPuppy}
}`,
codeArraysMatch: /Array/,
codeArrays: typeScriptWithConsoleLog(`// Array
const cats: Array = ['Simba', 'Aslan'];
// ${this.t.typeDoesSameThing}
const cats2: string[] = ['Simba', 'Aslan'];
interface Cat {
name: string,
age: number
}
const betterCats: Cat[] = [
{name: 'Simba', age: 22},
{name: 'Aslan', age: 9999}
];
console.log(betterCats);`),
code: `const price: number = 100; // ${this.t.thisIsNumber}
const tax = 20; // ${this.t.typescriptCanInferNumber}
const productName = 'pikachu'; // ${this.t.typescriptCanInferString}
const isHungry = true; // Boolean
const weird = tax + isHungry; // ${this.t.cantAddNumAndBool}
tax.slice(1,5); // ${this.t.cantSliceNum}
productName.slice(1,5); // ${this.t.canSliceString}
const total = price + tax; // ${this.t.works}`
},
varDeclaration: {
code: `// ${this.t.varAllowedNotRecommended}
var v = 1;
// ${this.t.letInsteadOfVar}
let l = 1;
if(true){
let ll = 1; // ${this.t.letUnavailableOutsideIfUnlikeIf}
}
console.log(ll); // undefined
// ${this.t.constLikeLet}
const x = 1;
x = 2;`
},
stringType: {
code: `let fullName: string = 'Bob Bobbington';
let sentence: string = \`Hello, my name is \${ fullName }.\`;`
},
stringType2: {
code: `let sentence: string = "Hello, my name is " + fullName + "."`
},
anyType: {
code: `let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // ${this.t.definitelyBoolean}`
},
classDescription: {
code: typeScriptWithConsoleLog(`export class Puppy {
// ${this.t.commonDogName}
name = 'Bar Boss';
// ${this.t.thisIsMethod}
bark(){
// ${this.t.thatsHowRussianDogsTalk}
return this.name + ': Gav gav!!';
}
}
// ${this.t.nowWeCanInstantiate}
var hotdog = new Puppy();
// ${this.t.andUseItsMethods}
console.log(hotdog.bark());
`),
codeConstructor: typeScriptWithConsoleLog(`export class Puppy {
constructor(public name: string){
// ${this.t.laterWeWillHaveCode}
}
bark(){
return 'Gav! my name is ' + this.name;
}
}
var hotdog = new Puppy('Édouard');
console.log(hotdog.bark());
// ${this.t.letsCreateMorePuppies}
var oscar = new Puppy('Oscar-Claude');
console.log(oscar.bark());`),
codeExport: typeScriptWithConsoleLog(`export class Puppy {
constructor(public name: string){}
bark(){
return 'Gav! my name is ' + this.name;
}
}`),
codeImport: typeScriptWithConsoleLog(
`import {Puppy} from './puppy';
var hotdog = new Puppy('Édouard');
console.log(hotdog.bark());
// ${this.t.letsCreateMorePuppies}
var oscar = new Puppy('Oscar-Claude');
console.log(oscar.bark());`,
'import "./app";',
undefined,
`export class Puppy {
constructor(public name: string){}
bark(){
return 'Gav! my name is ' + this.name;
}
}`
),
matches: {
classPuppyMatch: { 'app.ts': /class Puppy/ },
classMatch: /class/,
exportMatch: /export/,
importMatch: {
'puppy.ts': /export/,
'app.ts': /import/
},
arrayMatch: {
'app.ts': [/Array/, /string\[]/]
},
constants: /const /,
constructorMatch: { 'app.ts': [/(public name: string)/, /Édouard/] },
modifierMatch: { 'app.ts': [/public name/, /this.name/] },
oscarMatch: /Oscar-Claude/
}
},
tsExercise: typeScriptWithConsoleLog(
`function add(a: number, b){
return a+b;
};
console.log(add(2, '2'));`,
undefined,
require(`!raw-loader!./code/mini-exercise-test.ts`)
.replace('@@specifyTheTypeForB', this.t.specifyTheTypeForB)
.replace(
'@@typescriptHighlightsErrorFix224',
this.t.typescriptHighlightsErrorFix224
)
),
js2And2: (() => {
const code = javaScriptWithConsoleLog(
`function add(a, b){
return a+b;
};
console.log(add(2, '2'));`
);
(code.files[2] as any).bootstrap = false;
return code;
})(),
tsExerciseMatch: { 'app.ts': /'.*'/ }
};
}
}
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript-routing.module.ts
================================================
import { Component, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SlidesRoutes } from '@ng360/slides';
import { TypeScriptComponent } from './typescript/typescript.component';
@Component({
// tslint:disable-next-line:component-selector
selector: 'ignored',
template: ' '
})
export class EmptyTypeScriptComponent {}
const routes = [
{
path: '',
component: EmptyTypeScriptComponent,
children: [...SlidesRoutes.get(TypeScriptComponent)]
}
];
@NgModule({
declarations: [EmptyTypeScriptComponent],
entryComponents: [EmptyTypeScriptComponent],
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class TypeScriptRoutingModule {}
================================================
FILE: apps/codelab/src/app/codelabs/angular/typescript/typescript.module.ts
================================================
import { NgModule } from '@angular/core';
import { TypeScriptComponent } from './typescript/typescript.component';
import { TypeScriptRoutingModule } from './typescript-routing.module';
import { TypeScriptSvgComponent } from './typescript/typescript-svg/typescript-svg.component';
import { SharedModule } from '../../../shared/shared.module';
@NgModule({
declarations: [TypeScriptComponent, TypeScriptSvgComponent],
imports: [SharedModule, TypeScriptRoutingModule]
})
export class TypeScriptModule {}
================================================
FILE: apps/codelab/src/app/codelabs/codelabs-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { IndexComponent } from '../components/index/index.component';
const routes: Routes = [
{ path: '', component: IndexComponent },
{
path: 'angular',
loadChildren: () =>
import('./angular/angular.module').then(m => m.AngularModule)
},
{
path: 'extra',
loadChildren: () => import('./extra/extra.module').then(m => m.ExtraModule)
},
{
path: 'about',
loadChildren: () => import('./about/about.module').then(m => m.AboutModule)
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class CodelabsRoutingModule {}
================================================
FILE: apps/codelab/src/app/codelabs/codelabs.module.ts
================================================
import { NgModule } from '@angular/core';
import { CodelabsRoutingModule } from './codelabs-routing.module';
import { IndexModule } from '../components/index/index.module';
@NgModule({
imports: [CodelabsRoutingModule, IndexModule]
})
export class CodelabsModule {}
================================================
FILE: apps/codelab/src/app/codelabs/extra/code-playground/code-playground.component.css
================================================
.vscode-gifs img {
width: 100%;
height: auto;
max-height: 100%;
}
.feature-title {
text-align: center;
font-style: italic;
margin: 20px 1px 1px 1px;
}
.vscode-gifs {
display: inline-block;
padding: 5px;
}
================================================
FILE: apps/codelab/src/app/codelabs/extra/code-playground/code-playground.component.html
================================================
================================================
FILE: apps/codelab/src/app/codelabs/extra/code-playground/code-playground.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-code-playground',
templateUrl: './code-playground.component.html',
styleUrls: ['./code-playground.component.css']
})
export class CodePlaygroundComponent {}
//
//
// // 1. code- components
// //
// //
// //
// //
// //
// //
// //
// //
// //
// //
// //
//
//
// /// TypeScript
// /// Javasdcript
//
// @Component({
// selector: 'codelab-exercise',
// template: ' ',
// providers: [CodeSource]
// })
// class CodelabExercise {
// constructor(cs: CodeSource) {
// cs.addBeforeStep((cide) => {
// code.before = 1
// })
// }
//
// getRoot(){
// return this.parent || this;
// }
//
// addCode(code: Record) {
// this.code = code;
// }
//
// getCode() {
// if (this.code) {
// return this.code;
// }
// return this.parent.getCode();
// }
//
// getDeps() {
// return this.parent && this.parent.getDeps() + this.deps;
// }
//
// }
//
//
// @Component({
// selector: 'code-group',
// template: ' ',
// providers: [CodeSource]
// })
// class CodeGroup {
// // { "main.ts": "console.log('')" }
// @Input() code: Record;
// // { "main.ts": "console.log('Hello')" }
// @Input() solution: Record;
// // // "main.ts"
// // @Input() bootstrap: string;
// // "angular, react"
// @Input() deps: string;
// @Input() compile: (code: Record) => Record;
// // "browser, tests"
// @Input() ui: string;
// }
//
// @Component({
// selector: 'code-preview',
// template: '',
// providers: [CodeSource]
// })
// class CodePreview {
// constructor(private source: CodeSource) {
// }
// }
================================================
FILE: apps/codelab/src/app/codelabs/extra/code-playground/code-playground.module.ts
================================================
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { CodePlaygroundComponent } from './code-playground.component';
const routes = RouterModule.forChild(SlidesRoutes.get(CodePlaygroundComponent));
@NgModule({
imports: [routes, SlidesModule, FeedbackModule, CommonModule],
declarations: [CodePlaygroundComponent],
exports: [CodePlaygroundComponent]
})
export class CodePlaygroundModule {}
================================================
FILE: apps/codelab/src/app/codelabs/extra/extra-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { environment } from '../../../environments/environment';
import { FullLayoutComponent } from '../../containers/full-layout';
let routes = [
{
path: '',
component: FullLayoutComponent,
children: [
{
path: 'code-playground',
loadChildren: () =>
import('./code-playground/code-playground.module').then(
m => m.CodePlaygroundModule
),
name: 'code-playground',
description:
'Learn how pipes transform input values to output values for display in a view',
page: 'extra'
},
{
path: 'rating-summary',
loadChildren: () =>
import('./rating-summary/rating-summary.module').then(
m => m.RatingSummaryModule
),
name: 'rating-summary',
description:
'Learn how pipes transform input values to output values for display in a view',
page: 'extra'
}
]
}
];
if (environment.production) {
routes = routes.filter(r => r['prod']);
}
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ExtraRoutingModule {}
================================================
FILE: apps/codelab/src/app/codelabs/extra/extra.module.ts
================================================
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { CommonModule } from '@angular/common';
import { SharedModule } from '../../shared/shared.module';
import { ExtraRoutingModule } from './extra-routing.module';
import { FullLayoutModule } from '../../containers/full-layout/full-layout.module';
@NgModule({
imports: [
SharedModule,
ExtraRoutingModule,
CommonModule,
HttpClientModule,
FormsModule,
FullLayoutModule
]
})
export class ExtraModule {}
================================================
FILE: apps/codelab/src/app/codelabs/extra/rating-summary/rating-summary.component.css
================================================
.ratingsummary {
text-align: center;
margin-left: auto;
margin-right: auto;
margin-top: 60px;
border: 1px solid slategray;
padding: 20px;
}
================================================
FILE: apps/codelab/src/app/codelabs/extra/rating-summary/rating-summary.component.html
================================================
================================================
FILE: apps/codelab/src/app/codelabs/extra/rating-summary/rating-summary.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'codelab-slides-rating-summary',
templateUrl: './rating-summary.component.html',
styleUrls: ['./rating-summary.component.css']
})
export class RatingSummaryComponent implements OnInit {
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/extra/rating-summary/rating-summary.module.ts
================================================
import { AngularFireModule } from '@angular/fire';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { HttpClientModule } from '@angular/common/http';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { RatingSummaryComponent } from './rating-summary.component';
import { environment } from '../../../../environments/environment';
const routes = RouterModule.forChild(SlidesRoutes.get(RatingSummaryComponent));
export const angularFire = AngularFireModule.initializeApp(
environment.firebaseConfig
);
@NgModule({
imports: [
routes,
BrowserWindowModule,
angularFire,
CommonModule,
HttpClientModule,
FeedbackModule,
SlidesModule,
AngularFireDatabaseModule,
AngularFireAuthModule
],
declarations: [RatingSummaryComponent],
providers: [],
exports: [RatingSummaryComponent]
})
export class RatingSummaryModule {}
================================================
FILE: apps/codelab/src/app/codelabs/extra/visual-studio-code/visual-studio-code.component.css
================================================
.vscode-gifs img {
width: 100%;
height: auto;
max-height: 100%;
}
.feature-title {
text-align: center;
font-style: italic;
margin: 20px 1px 1px 1px;
}
.vscode-gifs {
display: inline-block;
padding: 5px;
}
================================================
FILE: apps/codelab/src/app/codelabs/extra/visual-studio-code/visual-studio-code.component.html
================================================
What is Visual Studio Code?
Visual Studio Code is a free, open-source code editor developed by
Microsoft
built with Github Electron, and written in HTML, CSS,and Javascript
offers a robust extensible architecture and is highly customizable
built-in perks include:
Source Control
Extensions Marketplace
Debugger
Built-in Terminal
Why Use Visual Studio Code?
It has awesome tooling support and works especially well with TypeScript.
It comes with Intellisense , which provides code completion and
code info.
No Really, Why Use Visual Studio Code?
Missing Reference? Syntax Error? No problem!
Look for the lightbulb to shed some light on the issue and maybe even
automatically fix it for you.
OK, How Do I Get VSCode?
Interested? Curious?
Ready to join the cult?
Click
here to
download.
Run the installer
Enjoy!
Profit???
Popular extensions
are many extensions available for VSCode. Some good ones
include:
Common Keyboard Shortcuts
Formats your code (Shift+Alt+F)
Open New Window (Ctrl+Shift+N)
Open Project Folder (Ctrl+K,Ctrl+O)
Go to File (Ctrl+P)
Expand Selection (Shift+Alt+Right)
Search multiple File (Ctrl+Shift+F)
Go to Definition (F12)
Rename (F2)
Multiple Cursor(Alt+Mouse)
See more
here
Miss your old editor keymap? Try out of one of these extensions:
Vim ,
Atom ,
Sublime
What!? you forgot all the shortcuts already?
No problem, VSCode has your back. Hit (F1) or (ctrl+shift+p) and type the
command you want to perform
Well done! This is the end of the milestone!
================================================
FILE: apps/codelab/src/app/codelabs/extra/visual-studio-code/visual-studio-code.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'codelab-slides-visual-studio-code',
templateUrl: './visual-studio-code.component.html',
styleUrls: ['./visual-studio-code.component.css']
})
export class VisualStudioCodeComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/codelabs/extra/visual-studio-code/visual-studio-code.module.ts
================================================
import { RouterModule } from '@angular/router';
import { FeedbackModule } from '@codelab/feedback';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { VisualStudioCodeComponent } from './visual-studio-code.component';
import { CodelabComponentsModule } from '../../../components/codelab-components.module';
const routes = RouterModule.forChild(
SlidesRoutes.get(VisualStudioCodeComponent)
);
@NgModule({
imports: [
SlidesModule,
CodelabComponentsModule,
routes,
FeedbackModule,
CommonModule
],
declarations: [VisualStudioCodeComponent],
exports: [VisualStudioCodeComponent]
})
export class VisualStudioCodeModule {}
================================================
FILE: apps/codelab/src/app/common.ts
================================================
import { InjectionToken } from '@angular/core';
import { Route } from '@angular/router';
export type MenuRoutes = MenuRoute[];
interface MenuRoute extends Route {
name?: string;
description?: string;
page?: string;
prod?: boolean;
translationIds?: string[];
children?: MenuRoutes;
}
export const MENU_ROUTES = new InjectionToken('menuRoutes');
================================================
FILE: apps/codelab/src/app/components/angular-routes/angular-routes.component.html
================================================
Lessons
{{ route.description }}
================================================
FILE: apps/codelab/src/app/components/angular-routes/angular-routes.component.scss
================================================
:host {
display: flex;
flex-direction: column;
align-items: center;
margin: 2rem;
font-family: 'Helvetica Neue', sans-serif;
font-size: 30px;
ol {
margin-block-start: 0;
h2 {
font-size: 1.2em;
margin-block-end: 0.5em;
}
}
}
@media screen and (max-width: 500px) {
:host {
font-size: 20px;
}
}
================================================
FILE: apps/codelab/src/app/components/angular-routes/angular-routes.component.ts
================================================
import { ChangeDetectionStrategy, Component, Inject } from '@angular/core';
import { MENU_ROUTES } from '../../common';
@Component({
selector: 'codelab-angular-routes',
templateUrl: 'angular-routes.component.html',
styleUrls: ['angular-routes.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AngularRoutesComponent {
constructor(@Inject(MENU_ROUTES) readonly menuRoutes) {}
}
================================================
FILE: apps/codelab/src/app/components/angular-routes/angular-routes.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { AngularRoutesComponent } from './angular-routes.component';
@NgModule({
imports: [CommonModule, RouterModule],
declarations: [AngularRoutesComponent],
exports: [AngularRoutesComponent]
})
export class AngularRoutesModule {}
================================================
FILE: apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.css
================================================
.runner {
display: none;
}
================================================
FILE: apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/angular-test-runner/angular-test-runner.component.ts
================================================
import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
ViewChild
} from '@angular/core';
import { createSystemJsSandbox } from '@codelab/code-demos/src/lib/shared/sandbox';
import { ScriptLoaderService } from '@codelab/code-demos/src/lib/shared/script-loader.service';
import babel_traverse from '@babel/traverse';
import * as babylon from 'babylon';
import * as babel_types from 'babel-types';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { Subscription } from 'rxjs/internal/Subscription';
import { getTypeScript } from '@codelab/utils/src/lib/loaders/loaders';
import { TestRunResult } from '@codelab/utils/src/lib/test-results/common';
import { handleTestMessage } from './tests';
const ts = getTypeScript();
// TODO(kirjs): This is a duplicate
export function addMetaInformation(sandbox, files: { [key: string]: string }) {
sandbox.evalJs(`System.registry.delete(System.normalizeSync('./code'));`);
(sandbox.iframe.contentWindow as any).System.register('code', [], function(
exports
) {
return {
setters: [],
execute: function() {
exports('ts', ts);
exports('babylon', babylon);
exports('babel_traverse', babel_traverse);
exports('babel_types', babel_types);
Object.entries(files)
.filter(([moduleName]) => moduleName.match(/\.ts$/))
.forEach(([path, code]) => {
exports(path.replace(/[\/.-]/gi, '_'), code);
exports(
path.replace(/[\/.-]/gi, '_') + '_AST',
ts.createSourceFile(path, code, ts.ScriptTarget.ES5)
);
});
Object.entries(files)
.filter(([moduleName]) => moduleName.match(/\.html/))
.forEach(([path, code]) => {
const templatePath = path.replace(/[\/.-]/gi, '_');
exports(templatePath, code);
});
}
};
});
}
@Component({
selector: 'codelab-simple-angular-test-runner',
templateUrl: './angular-test-runner.component.html',
styleUrls: ['./angular-test-runner.component.css']
})
export class SimpleAngularTestRunnerComponent
implements OnInit, OnChanges, AfterViewInit, OnDestroy {
handleMessageBound: any;
@Output() solved: EventEmitter = new EventEmitter();
@Output() public selectFile: EventEmitter = new EventEmitter<
string
>();
@Input() translations: { [key: string]: string } = {};
@Input() code: any;
@Input() bootstrap: string;
@ViewChild('runner', { static: false }) runnerElement: ElementRef;
changedFilesSubject = new BehaviorSubject>({});
tests: any;
private subscription: Subscription;
result: TestRunResult = { tests: [] };
constructor(
private scriptLoaderService: ScriptLoaderService,
private cd: ChangeDetectorRef
) {}
ngOnInit() {
this.handleMessageBound = message => {
this.tests = handleTestMessage(message, this.tests || []);
this.result = {
tests: this.tests.map(test => {
const name =
this.translations[test.title.replace('@@', '')] || test.title;
return { ...test, name, error: test.result };
})
};
if (message.data.type === 'testEnd') {
this.solved.emit(
this.tests.length > 0 && this.tests.every(test => test.pass)
);
}
this.cd.markForCheck();
};
window.addEventListener('message', this.handleMessageBound, false);
}
ngOnChanges(changes: SimpleChanges) {
if (changes.code) {
this.changedFilesSubject.next(changes.code.currentValue);
}
}
async ngAfterViewInit() {
const sandbox = await createSystemJsSandbox(
this.runnerElement.nativeElement,
{
id: 'testing',
url: '/assets/runner'
}
);
sandbox.setHtml(
this.code['index.html'] ||
'
'
);
sandbox.evalJs(this.scriptLoaderService.getScript('chai'));
sandbox.evalJs(this.scriptLoaderService.getScript('mocha'));
sandbox.evalJs(this.scriptLoaderService.getScript('test-bootstrap'));
sandbox.evalJs(this.scriptLoaderService.getScript('shim'));
sandbox.evalJs(this.scriptLoaderService.getScript('zone'));
sandbox.evalJs(this.scriptLoaderService.getScript('system-config'));
sandbox.evalJs(this.scriptLoaderService.getScript('ng-bundle'));
this.subscription = this.changedFilesSubject.subscribe(files => {
const hasErrors = Object.entries(files)
.filter(([path]) => path.match(/\.js$/))
.map(([path, code]) => {
try {
sandbox.evalJs(
`System.registry.delete(System.normalizeSync('./${path.replace(
'.js',
''
)}'));`
);
addMetaInformation(sandbox, this.code);
sandbox.evalJs(code);
} catch (e) {
console.groupCollapsed(e.message);
console.log(e);
console.groupEnd();
return true;
}
return false;
})
.some(a => a);
if (!hasErrors) {
sandbox.evalJs(`System.import('${this.bootstrap}')`);
}
});
}
ngOnDestroy() {
if (this.subscription) {
this.subscription.unsubscribe();
this.subscription = null;
}
}
}
================================================
FILE: apps/codelab/src/app/components/angular-test-runner/tests.ts
================================================
export function handleTestMessage(message, tests) {
if (!message.data || !message.data.type) {
return tests;
}
if (message.data.type === 'testList') {
return message.data.tests.map(test => ({
title: test
}));
}
if (message.data.type === 'testEnd') {
return tests;
}
if (message.data.type === 'testResult') {
return tests.map(test => {
if (test.title === message.data.test.title) {
test.pass = message.data.pass;
test.result = message.data.result;
}
return test;
});
}
return tests;
}
================================================
FILE: apps/codelab/src/app/components/babel-test-runner/babel-helpers.ts
================================================
import * as T from 'babel-types';
import * as babylon from 'babylon';
import babel_traverse from '@babel/traverse';
import { getTypeScript } from '@codelab/utils/src/lib/loaders/loaders';
import * as TsTypes from 'typescript';
const ts = getTypeScript();
function matchesValue(actual, expected) {
if (!actual) {
return false;
}
if (expected instanceof RegExp) {
return expected.test(actual);
}
return actual.trim() === expected.trim();
}
export const expectClass = name => ({ node, parent }) =>
T.isIdentifier(node, { name }) &&
T.isClassDeclaration(parent, { superClass: null });
export const expectExportedClass = name => ({ node, parent, parentPath }) =>
expectClass(name)({ node, parent }) &&
T.isExportNamedDeclaration(parentPath.parent);
export const expectDecorator = name => ({ node }) =>
T.isDecorator(node) && node.expression.callee.name === name;
export const expectDecoratorPropertyStringValue = (
decoratorName,
keyName,
value
) => path => {
function matchesTemplateLiteral() {
return (
T.isTemplateLiteral(path.node) &&
matchesValue(path.node.quasis[0].value.raw, value)
);
}
function matchesStringLiteral() {
return T.isStringLiteral(path.node) && matchesValue(path.node.value, value);
}
return (
(matchesTemplateLiteral() || matchesStringLiteral()) &&
T.isObjectProperty(path.parent) &&
path.parent.key.name === keyName &&
path.findParent(T.isDecorator).node.expression.callee.name === decoratorName
);
};
export function babelTestSuite(filePath, tests) {
return function test(files) {
const results = tests.map(({ title }) => ({ title, pass: false }));
const code = files[filePath];
const ast = babylon.parse(code, {
sourceType: 'module',
plugins: ['decorators']
});
babel_traverse(ast, {
enter(path) {
tests.forEach((testConfig, index) => {
try {
results[index].pass =
results[index].pass || testConfig.condition(path);
} catch (e) {
console.log(e);
}
});
}
});
return results;
};
}
export type Predicate = (node: TsTypes.Node) => boolean;
export class MiniTsQuery {
constructor(private readonly ast: TsTypes.Node) {}
some(predicate: Predicate) {
let result = false;
this.traverse(this.ast, node => {
if (predicate(node)) {
result = true;
}
});
return result;
}
findOne(predicate) {
let result;
this.traverse(this.ast, node => {
if (!result && predicate(node)) {
result = node;
}
});
return result;
}
hasOne(predicate) {
return !!this.findOne(predicate);
}
hasIdentifier(identifier: string) {
return !!this.getIdentifier(identifier);
}
getIdentifier(identifier: string) {
return this.findOne(
node => ts.isIdentifier(node) && node.text === identifier
);
}
hasConstructorParam(identifier: string, type: string) {
const node = this.findOne(
node => ts.isIdentifier(node) && node.text === identifier
);
return node ? node.parent.type.typeName.text === type : undefined;
}
hasVariableDeclaration(identifier: string) {
const node = this.getIdentifier(identifier);
return node && ts.isVariableDeclaration(node.parent) ? node : undefined;
}
hasDecorator(type: string) {
return !!this.getDecorator(type);
}
hasDecoratorValue(decoratorType: string, property: string, type: string) {
const decorator = this.getDecorator(decoratorType);
if (
decorator.expression &&
ts.isCallExpression(decorator.expression) &&
decorator.expression.arguments &&
decorator.expression.arguments[0] &&
ts.isObjectLiteralExpression(decorator.expression.arguments[0])
) {
return (decorator.expression.arguments[0] as any).properties
.filter(prop => prop && prop.name && prop.name.text === property)
.some(arrayValue =>
arrayValue.initializer.elements.some(a => a.text === type)
);
}
return false;
}
hasProvider(type: string) {
return this.hasDecoratorValue('NgModule', 'providers', 'VideoService');
}
getDecorator(type: string): TsTypes.Decorator {
return this.findOne(node => {
return (
ts.isDecorator(node) &&
node.expression &&
ts.isCallExpression(node.expression) &&
node.expression.expression &&
ts.isIdentifier(node.expression.expression) &&
node.expression.expression.text === type
);
});
}
private traverse(node, callback) {
callback(node);
ts.forEachChild(node, node => this.traverse(node, callback));
}
}
export function tsAstTestSuite(tests) {
return function test(files) {
const results = tests.map(({ title }) => ({ title, pass: false }));
const astCache = {};
function getAst(filePath) {
if (!astCache[filePath]) {
astCache[filePath] = new MiniTsQuery(
ts.createSourceFile(
filePath,
files[filePath],
ts.ScriptTarget.ES2015,
/*setParentNodes */ true
)
);
}
return astCache[filePath];
}
tests.forEach((testConfig, index) => {
try {
if (!testConfig.file) {
throw new Error('test must specify a file');
}
results[index].pass =
results[index].pass || testConfig.condition(getAst(testConfig.file));
} catch (e) {
console.log(e);
}
});
return results;
};
}
================================================
FILE: apps/codelab/src/app/components/babel-test-runner/babel-test-runner.component.css
================================================
.runner {
display: none;
}
================================================
FILE: apps/codelab/src/app/components/babel-test-runner/babel-test-runner.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/babel-test-runner/babel-test-runner.component.ts
================================================
import {
AfterViewInit,
Component,
EventEmitter,
Input,
OnChanges,
Output,
SimpleChanges
} from '@angular/core';
import { TestInfo } from '../../shared/interfaces/test-info';
import { FileConfig } from '../../shared/interfaces/file-config';
import { TestRunResult } from '@codelab/utils/src/lib/test-results/common';
declare const require;
@Component({
selector: 'codelab-babel-test-runner',
templateUrl: './babel-test-runner.component.html',
styleUrls: ['./babel-test-runner.component.css']
})
export class BabelTestRunnerComponent implements AfterViewInit, OnChanges {
@Input() bootstrap: string;
tests: Array = [];
@Input() translations: { [key: string]: string } = {};
@Input() code: any;
@Output() solved: EventEmitter = new EventEmitter();
constructor() {}
result: TestRunResult = { tests: [] };
ngOnChanges(changes: SimpleChanges) {
if (changes.code) {
this.run(this.code);
}
}
run(files: any) {
const test = files[this.bootstrap + '.ts.execute'];
try {
this.tests = test(files);
this.result = {
tests: this.tests.map(test => {
const name =
this.translations[test.title.replace('@@', '')] || test.title;
return {
...test,
name,
error: test.result,
pass: !!test.pass
};
})
};
const isSolved = this.result.tests.every(a => a.pass);
this.solved.emit(isSolved);
} catch (e) {
this.tests.find(t => !t.pass).result = '[Parsing error]' + e;
}
}
selectFile(file: FileConfig) {
// this.parent.currentFile = file;
}
ngAfterViewInit(): void {
// this.parent.files$.subscribe(files => requestAnimationFrame(() => this.run(files)));
}
}
================================================
FILE: apps/codelab/src/app/components/breadcrumb/breadcrumb.component.css
================================================
a {
text-decoration: none;
font-weight: 300;
}
span {
cursor: pointer;
}
span.arrow {
font-size: 0.5em;
vertical-align: middle;
}
================================================
FILE: apps/codelab/src/app/components/breadcrumb/breadcrumb.component.html
================================================
Angular Codelab {{ separator }}
{{ active }} ▼
{{ separator }}
{{ milestone.name }}
================================================
FILE: apps/codelab/src/app/components/breadcrumb/breadcrumb.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BreadcrumbComponent } from './breadcrumb.component';
import { ActivatedRoute } from '@angular/router';
import { MENU_ROUTES } from '../../common';
import { CodelabComponentsModule } from '../codelab-components.module';
import { RouterTestingModule } from '@angular/router/testing';
describe('BreadcrumbComponent', () => {
let component: BreadcrumbComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [CodelabComponentsModule, RouterTestingModule],
providers: [
{
provide: ActivatedRoute,
useValue: {
pathFromRoot: [{ routeConfig: { name: 'lol' } }]
}
},
{
provide: MENU_ROUTES,
useValue: []
}
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BreadcrumbComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/components/breadcrumb/breadcrumb.component.ts
================================================
import { Component, Inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { MENU_ROUTES } from '../../common';
@Component({
selector: 'codelab-breadcrumb',
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.css']
})
export class BreadcrumbComponent {
active: string;
readonly separator = '/';
constructor(
private activatedRoute: ActivatedRoute,
@Inject(MENU_ROUTES) readonly menuRoutes
) {
this.active = this.activatedRoute.pathFromRoot.find(
route => route.routeConfig && route.routeConfig['name']
).routeConfig['name'];
}
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/buttons-nav-bar.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/buttons-nav-bar.component.scss
================================================
:host ::ng-deep {
.btn-bar {
z-index: 110;
}
button:focus {
outline: 0;
}
.menu-bar-btn {
width: 45px;
height: 45px;
border-radius: 50%;
background: no-repeat center white;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
margin-left: 10px;
cursor: pointer;
border: none;
padding: 0;
}
}
::ng-deep {
.modal-title {
margin: 0 0 10px 0;
}
.buttons-nav-bar-modal-content-wrapper {
padding: 8px;
font-weight: 300;
font-family: 'Helvetica Neue', sans-serif;
}
@media screen and (max-width: 350px) {
.cdk-overlay-pane {
width: 100%;
}
.buttons-nav-bar-modal-content-wrapper {
width: 100%;
}
}
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/buttons-nav-bar.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-buttons-nav-bar',
templateUrl: './buttons-nav-bar.component.html',
styleUrls: ['./buttons-nav-bar.component.scss']
})
export class ButtonsNavBarComponent {}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/buttons-nav-bar.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ButtonsNavBarComponent } from './buttons-nav-bar.component';
import { FeedbackModule } from '@codelab/feedback';
import { FirebaseLoginModule } from '@codelab/firebase-login';
import { MenuGithubWidgetModule } from './menu-github-widget/menu-github-widget.module';
import { MenuShortcutWidgetModule } from './menu-shortcut-widget/menu-shortcut-widget.module';
import { MenuFullscreenWidgetComponent } from './menu-fullscreen-widget/menu-fullscreen-widget.component';
import { DirectivesModule } from '../../directives/directives.module';
@NgModule({
imports: [
CommonModule,
FeedbackModule,
FirebaseLoginModule,
MenuGithubWidgetModule,
MenuShortcutWidgetModule,
DirectivesModule
],
declarations: [ButtonsNavBarComponent, MenuFullscreenWidgetComponent],
exports: [
ButtonsNavBarComponent,
FeedbackModule,
FirebaseLoginModule,
MenuGithubWidgetModule,
MenuShortcutWidgetModule
]
})
export class ButtonsNavBarModule {}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-fullscreen-widget/menu-fullscreen-widget.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-fullscreen-widget/menu-fullscreen-widget.component.scss
================================================
.menu-bar-btn {
width: 45px;
height: 45px;
border-radius: 50%;
background: no-repeat center #ecf0f1;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
margin-left: 10px;
cursor: pointer;
border: none;
padding: 0;
img {
width: 60%;
}
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-fullscreen-widget/menu-fullscreen-widget.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MenuFullscreenWidgetComponent } from './menu-fullscreen-widget.component';
describe('MenuFullscreenWidgetComponent', () => {
let component: MenuFullscreenWidgetComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MenuFullscreenWidgetComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MenuFullscreenWidgetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-fullscreen-widget/menu-fullscreen-widget.component.ts
================================================
import { Component } from '@angular/core';
import { FullScreenModeService } from '@ng360/slides';
@Component({
selector: 'codelab-menu-fullscreen-widget',
templateUrl: './menu-fullscreen-widget.component.html',
styleUrls: ['./menu-fullscreen-widget.component.scss']
})
export class MenuFullscreenWidgetComponent {
constructor(private fullScreenService: FullScreenModeService) {}
openFullScreen() {
this.fullScreenService.toggleFullScreen();
}
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.component.css
================================================
.menu-bar-btn.github-widget {
background-color: white;
}
img {
width: 100%;
}
.octocat {
float: left;
width: 224px;
margin: 0 auto;
height: 224px;
background: url(images/octocat.png) no-repeat;
margin-right: 16px;
}
.content {
float: right;
width: 250px;
}
@media screen and (max-width: 350px) {
.octocat {
float: none;
margin: 5px auto;
display: block;
}
.content {
margin: 0 auto;
float: none;
text-align: center;
}
}
h3 {
font-weight: 300;
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.component.html
================================================
This Codelab is written in Angular!
It's 100% open-source and is available on
Github
Please ⭐ the repo if you find it useful.
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'codelab-menu-github-widget',
templateUrl: './menu-github-widget.component.html',
styleUrls: ['./menu-github-widget.component.css']
})
export class MenuGithubWidgetComponent {}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatMenuModule } from '@angular/material/menu';
import { MenuGithubWidgetComponent } from './menu-github-widget.component';
@NgModule({
imports: [CommonModule, MatMenuModule],
declarations: [MenuGithubWidgetComponent],
exports: [MenuGithubWidgetComponent]
})
export class MenuGithubWidgetModule {}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-shortcut-widget/menu-shortcut-widget.component.css
================================================
.menu-bar-btn.index-menu {
background-color: #ff594b;
}
ul,
li {
margin: 0;
padding: 0;
list-style-type: none;
}
a {
text-decoration: none;
cursor: pointer;
display: block;
padding: 6px 10px;
}
a:hover {
background-color: #982a2a;
color: white;
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-shortcut-widget/menu-shortcut-widget.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-shortcut-widget/menu-shortcut-widget.component.ts
================================================
import { Component, Inject } from '@angular/core';
import { MENU_ROUTES } from '../../../common';
@Component({
selector: 'codelab-menu-shortcut-widget',
templateUrl: './menu-shortcut-widget.component.html',
styleUrls: ['./menu-shortcut-widget.component.css']
})
export class MenuShortcutWidgetComponent {
constructor(@Inject(MENU_ROUTES) readonly menuRoutes) {}
}
================================================
FILE: apps/codelab/src/app/components/buttons-nav-bar/menu-shortcut-widget/menu-shortcut-widget.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { MatMenuModule } from '@angular/material/menu';
import { MenuShortcutWidgetComponent } from './menu-shortcut-widget.component';
@NgModule({
imports: [CommonModule, RouterModule, MatMenuModule],
declarations: [MenuShortcutWidgetComponent],
exports: [MenuShortcutWidgetComponent]
})
export class MenuShortcutWidgetModule {}
================================================
FILE: apps/codelab/src/app/components/codelab-components.module.ts
================================================
import { NgModule } from '@angular/core';
import { CodelabExerciseComponent } from './exercise/exercise.component';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatMenuModule } from '@angular/material/menu';
import { MatSelectModule } from '@angular/material/select';
import { RouterModule } from '@angular/router';
import { DirectivesModule } from '../directives/directives.module';
import { TitleSlideComponent } from './slides/title-slide/title-slide.component';
import { CodelabClosingSlideComponent } from './slides/closing-slide/codelab-closing-slide.component';
import { CodelabExercisePreviewComponent } from './exercise-preview/exercise-preview.component';
import { CodelabExercisePlaygroundComponent } from './exercise-playground/codelab-exercise-playground.component';
import { CodelabProgressBarComponent } from './codelab-progress-bar/codelab-progress-bar.component';
import { BabelTestRunnerComponent } from './babel-test-runner/babel-test-runner.component';
import { CodelabRippleAnimationComponent } from './slides/title-slide/ripple-animation/codelab-ripple-animation.component';
import { SimpleAngularTestRunnerComponent } from './angular-test-runner/angular-test-runner.component';
import { CodeDemoModule } from '@codelab/code-demos';
import { CodelabPreviewComponent } from './slides-preview/codelab-preview.component';
import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
import { TestResultsModule } from '@codelab/utils/src/lib/test-results/test-results.module';
@NgModule({
imports: [
CommonModule,
FormsModule,
RouterModule,
CodeDemoModule,
MatButtonModule,
MatMenuModule,
MatSelectModule,
TestResultsModule,
DirectivesModule
],
declarations: [
SimpleAngularTestRunnerComponent,
TitleSlideComponent,
BabelTestRunnerComponent,
BreadcrumbComponent,
CodelabExerciseComponent,
CodelabPreviewComponent,
CodelabClosingSlideComponent,
CodelabExercisePreviewComponent,
CodelabExercisePlaygroundComponent,
CodelabProgressBarComponent,
CodelabRippleAnimationComponent
],
exports: [
SimpleAngularTestRunnerComponent,
TitleSlideComponent,
BabelTestRunnerComponent,
BreadcrumbComponent,
CodelabExerciseComponent,
CodelabPreviewComponent,
CodelabClosingSlideComponent,
CodelabExercisePreviewComponent,
CodelabExercisePlaygroundComponent,
CodelabProgressBarComponent,
CodelabRippleAnimationComponent
]
})
export class CodelabComponentsModule {}
================================================
FILE: apps/codelab/src/app/components/codelab-progress-bar/codelab-progress-bar.component.css
================================================
.progress-bar {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 1;
}
.points-container {
display: flex;
height: 25px;
}
.points-container:hover .slide-block {
transition-duration: 0.5s;
height: 15px;
}
.slide-block {
flex-grow: 1;
height: 3px;
transition-duration: 0.5s;
background: #abb7b7;
}
.slide-block:hover {
background: #dadfe1;
}
.exercise-block {
background: #d35400;
}
.exercise-block:hover {
background: #f9690e;
}
.completed-slide {
background: #f89406;
}
.completed-slide:hover {
background: #e9d460;
}
.completed-slide.exercise-block {
background: #e67e22;
}
================================================
FILE: apps/codelab/src/app/components/codelab-progress-bar/codelab-progress-bar.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/codelab-progress-bar/codelab-progress-bar.component.ts
================================================
import { AfterViewInit, Component } from '@angular/core';
import { SlidesDeckComponent } from '@ng360/slides';
@Component({
selector: 'codelab-progress-bar',
templateUrl: './codelab-progress-bar.component.html',
styleUrls: ['./codelab-progress-bar.component.css']
})
export class CodelabProgressBarComponent implements AfterViewInit {
slides = [];
activeSlideIndex = 0;
tempSlideId = 0;
constructor(public deck: SlidesDeckComponent) {}
ngAfterViewInit() {
// Change detection complains if updating it right away.
requestAnimationFrame(() => {
this.slides = this.deck.slides;
this.activeSlideIndex = this.deck.activeSlideIndex;
});
this.deck.slideChange.subscribe(index => {
this.activeSlideIndex = index;
});
}
previewSlide(index) {
this.tempSlideId = this.activeSlideIndex;
this.deck.goToSlide(index);
}
goToSlide(index) {
this.deck.goToSlide(index);
this.tempSlideId = this.activeSlideIndex;
}
}
================================================
FILE: apps/codelab/src/app/components/css/codelab-styles.scss
================================================
code-demo-file-path {
display: block;
margin: 1px 0;
}
================================================
FILE: apps/codelab/src/app/components/exercise/exercise.component.css
================================================
.quarter {
width: 50vw;
height: 50vh;
}
.row {
display: flex;
}
.tests {
padding: 0.5vw;
box-sizing: border-box;
overflow-y: auto;
}
.solved-message {
margin-top: 16px;
}
.solved-message img {
display: block;
height: 280px;
margin-top: 16px;
}
================================================
FILE: apps/codelab/src/app/components/exercise/exercise.component.html
================================================
Well done! Now you can go to the next slide
================================================
FILE: apps/codelab/src/app/components/exercise/exercise.component.ts
================================================
import { ChangeDetectorRef, Component, Input } from '@angular/core';
import { convertExerciseToMap } from '../../../../../../ng2ts/ng2ts';
import { CodeDemoComponent } from '@codelab/code-demos/src/lib/code-demo/code-demo.component';
function filterByFileType(type: string, files: Record) {
return Object.entries(files).reduce((changedFiles, [path, code]) => {
if (path.match(new RegExp(`\\\.${type}$`))) {
changedFiles[path] = code;
}
return changedFiles;
}, {});
}
export function extractSolutions(files: any[]) {
return files.reduce((result, file) => {
if (file.solution) {
result[file.path] = file.solution || file.template;
}
return result;
}, {});
}
export function getChanges(current, previous) {
return Object.keys(current).reduce((changedFiles, path) => {
if (current[path] !== previous[path]) {
changedFiles[path] = current[path];
}
return changedFiles;
}, {});
}
@Component({
selector: 'codelab-exercise',
templateUrl: 'exercise.component.html',
styleUrls: ['./exercise.component.css']
})
export class CodelabExerciseComponent extends CodeDemoComponent {
isSolved = false;
constructor(private readonly cdr: ChangeDetectorRef) {
super();
}
showDogs() {
this.cdr.markForCheck();
}
@Input() set exercise(exercise) {
const map = convertExerciseToMap(exercise);
if (!this.highlights) {
this.highlights = map.highlights;
}
this.bootstrap = map.bootstrap;
this.bootstrapTest = map.bootstrapTest;
if (!this.files) {
this.files = [exercise.files[this.openFileIndex].path];
}
this.filesConfig = exercise;
this.solutions = extractSolutions(this.filesConfig.files);
this.code = map.code;
this.update(map.code);
}
retrieveFile(file, code) {
const f = this.filesConfig.files.find(f => f.path === file);
return (f.before || '') + code + (f.after || '');
}
}
================================================
FILE: apps/codelab/src/app/components/exercise-playground/codelab-exercise-playground.component.css
================================================
:host {
display: flex;
flex-direction: column;
}
================================================
FILE: apps/codelab/src/app/components/exercise-playground/codelab-exercise-playground.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/exercise-playground/codelab-exercise-playground.component.ts
================================================
import { Component, Input } from '@angular/core';
import { CodelabExerciseComponent } from '../exercise/exercise.component';
import { PreviewWindowType } from '@codelab/browser/src/lib/preview-window/preview-window.component';
@Component({
selector: 'codelab-exercise-playground',
templateUrl: 'codelab-exercise-playground.component.html',
styleUrls: ['codelab-exercise-playground.component.css']
})
export class CodelabExercisePlaygroundComponent extends CodelabExerciseComponent {
@Input() allowSwitchingFiles = false;
@Input() path = '';
@Input() ui: PreviewWindowType = 'browser';
}
================================================
FILE: apps/codelab/src/app/components/exercise-preview/exercise-preview.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/exercise-preview/exercise-preview.component.ts
================================================
import { Component, Input } from '@angular/core';
import {
CodelabExerciseComponent,
extractSolutions
} from '../exercise/exercise.component';
import { convertExerciseToMap } from '../../../../../../ng2ts/ng2ts';
@Component({
selector: 'codelab-exercise-preview',
templateUrl: 'exercise-preview.component.html'
// styleUrls: ['../exercise/code-demo-code-demo.component.css'],
})
export class CodelabExercisePreviewComponent extends CodelabExerciseComponent {
@Input() set exercise(exercise) {
const map = convertExerciseToMap(exercise);
this.filesConfig = exercise;
this.bootstrap = map.bootstrap;
this.code = extractSolutions(exercise.files);
this.update(this.code);
}
}
================================================
FILE: apps/codelab/src/app/components/external-link-directive/external-link-directive.directive.spec.ts
================================================
import { ExternalLinkDirectiveDirective } from './external-link-directive.directive';
describe('ExternalLinkDirectiveDirective', () => {
it('should create an instance', () => {
const directive = new ExternalLinkDirectiveDirective({
nativeElement: document.createElement('a')
});
expect(directive).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/components/external-link-directive/external-link-directive.directive.ts
================================================
import { Directive, Input, ElementRef } from '@angular/core';
@Directive({
// tslint:disable-next-line
selector: '[href]'
})
// TODO(meinou): Remove the second postfix
export class ExternalLinkDirectiveDirective {
constructor({ nativeElement }: ElementRef) {
nativeElement.target = '_blank';
}
}
================================================
FILE: apps/codelab/src/app/components/index/index.component.html
================================================
Create your first Angular app
Templates
Dependency-Injection
Component-Tree
Custom-Events
Angular is written in TypeScript, a superset of JavaScript. Learn
TypeScript.
Learn how to create and bootstrap your first Angular application
Learn how to use Angular templates
Learn how to provide dependencies to your code instead of hard-coding them
Learn how to structure your app with reusable components
Learn to bind to events.
Angular Codelab
Welcome to the interactive Angular Codelab. Here you can learn the basics
of Angular !
▶
Learn TypeScript
Skip if you're familiar with TypeScript
▶
Learn Angular
Or click here to see full contents...
================================================
FILE: apps/codelab/src/app/components/index/index.component.scss
================================================
.intro li a:hover {
text-decoration: underline;
}
.intro li a {
text-decoration: none;
color: #888;
}
.intro ul {
margin: 0;
padding: 0;
}
.intro li {
margin: 0;
padding: 0;
padding-left: 21px;
font-size: 1.3vw;
margin-bottom: 16px;
}
.intro li h2 {
font-size: 2vw;
}
.feedback-shift {
position: fixed;
right: 107px;
bottom: 1vh;
}
img .angular {
width: 30vw;
}
.learn-box:hover {
background: #eee;
}
.title {
color: #444;
}
.hint {
color: #666;
font-size: 3vw;
}
.learn-box.typescript {
opacity: 0.8;
}
.learn-box {
display: flex;
width: 100%;
font-size: 7vw;
justify-content: space-between;
background: #fafafa;
padding: 2vw;
margin-bottom: 2vw;
cursor: pointer;
border: 1px #999 solid;
border-radius: 10px;
}
.learn-box img {
width: 10vw;
height: 10vw;
font-size: 10px;
}
::ng-deep .slide {
display: block;
}
.play {
margin: 4vw 0;
}
.contents {
border-bottom: 1px #999 dashed;
display: inline-block;
cursor: pointer;
min-height: 20px;
}
::ng-deep {
.btn-bar {
z-index: 110;
}
.menu-bar-btn {
width: 45px;
height: 45px;
border-radius: 50%;
background: no-repeat center #3498db;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
margin-left: 10px;
cursor: pointer;
border: none;
padding: 0;
}
}
================================================
FILE: apps/codelab/src/app/components/index/index.component.ts
================================================
import { Component, ViewChild } from '@angular/core';
@Component({
selector: 'codelab-slides-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.scss']
})
export class IndexComponent {
@ViewChild('translations', { static: false }) translations;
showContents: boolean;
}
================================================
FILE: apps/codelab/src/app/components/index/index.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material/card';
import { SlidesModule } from '@ng360/slides';
import { IndexComponent } from './index.component';
import { ButtonsNavBarModule } from '../buttons-nav-bar/buttons-nav-bar.module';
import { CodelabComponentsModule } from '../codelab-components.module';
import { SyncModule } from '../../sync/sync.module';
import { AngularRoutesModule } from '../angular-routes/angular-routes.module';
@NgModule({
imports: [
CommonModule,
RouterModule,
ButtonsNavBarModule,
CodelabComponentsModule,
SlidesModule,
MatCardModule,
SyncModule,
AngularRoutesModule
],
declarations: [IndexComponent],
exports: [IndexComponent]
})
export class IndexModule {}
================================================
FILE: apps/codelab/src/app/components/login/login.component.css
================================================
================================================
FILE: apps/codelab/src/app/components/login/login.component.html
================================================
user: {{ user.displayName || user.email }}
uid: {{ loginService.uid$ | async }}
Login with Github
================================================
FILE: apps/codelab/src/app/components/login/login.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { getMockAngularFireProviders } from '@codelab/utils/src/lib/testing/mocks/angular-fire';
import { LoginComponent } from './login.component';
import { LoginModule } from './login.module';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [LoginModule],
providers: [...getMockAngularFireProviders()]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/components/login/login.component.ts
================================================
import { Component } from '@angular/core';
import { AngularFireAuth } from '@angular/fire/auth';
import { LoginService } from '@codelab/firebase-login';
import { auth } from 'firebase/app';
@Component({
selector: 'codelab-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent {
constructor(
private auth: AngularFireAuth,
readonly loginService: LoginService
) {}
login() {
this.auth.auth.signInWithPopup(new auth.GoogleAuthProvider());
}
}
================================================
FILE: apps/codelab/src/app/components/login/login.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { RouterModule } from '@angular/router';
import { LoginComponent } from './login.component';
@NgModule({
imports: [CommonModule, MatCardModule, RouterModule, MatButtonModule],
declarations: [LoginComponent]
})
export class LoginModule {}
================================================
FILE: apps/codelab/src/app/components/not-found/not-found.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/not-found/not-found.component.scss
================================================
:host {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100%;
font-family: 'Helvetica Neue', sans-serif;
.message {
display: flex;
justify-content: center;
align-items: center;
.error-code {
font-size: 70px;
margin-right: 20px;
}
.error-message {
.header {
font-size: 20px;
font-weight: 600;
}
.subheader {
font-size: 14px;
}
}
}
nav {
max-width: 100%;
width: 400px;
ul {
display: flex;
justify-content: space-around;
width: 100%;
padding-inline-start: 0;
li {
list-style-type: none;
}
}
}
}
@media screen and (max-width: 500px) {
.container .message {
flex-direction: column;
text-align: center;
}
}
================================================
FILE: apps/codelab/src/app/components/not-found/not-found.component.ts
================================================
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'codelab-not-found',
templateUrl: './not-found.component.html',
styleUrls: ['./not-found.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NotFoundComponent {
public routes = [
{
path: '',
name: 'Home'
},
{
path: '/angular/typescript',
name: 'Typescript'
},
{
path: '/angular',
name: 'Angular'
}
];
}
================================================
FILE: apps/codelab/src/app/components/not-found/not-found.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { ButtonsNavBarModule } from '../buttons-nav-bar/buttons-nav-bar.module';
import { NotFoundComponent } from './not-found.component';
@NgModule({
imports: [CommonModule, RouterModule, ButtonsNavBarModule],
declarations: [NotFoundComponent]
})
export class NotFoundModule {}
================================================
FILE: apps/codelab/src/app/components/slides/closing-slide/codelab-closing-slide.component.css
================================================
================================================
FILE: apps/codelab/src/app/components/slides/closing-slide/codelab-closing-slide.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/slides/closing-slide/codelab-closing-slide.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CodelabClosingSlideComponent } from './codelab-closing-slide.component';
describe('CodelabClosingSlideComponent', () => {
let component: CodelabClosingSlideComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CodelabClosingSlideComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CodelabClosingSlideComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/components/slides/closing-slide/codelab-closing-slide.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'codelab-closing-slide',
templateUrl: './codelab-closing-slide.component.html',
styleUrls: ['./codelab-closing-slide.component.css']
})
export class CodelabClosingSlideComponent implements OnInit {
@Input() header: String;
@Input() body: String;
@Input() footer: String;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/ripple-animation/codelab-ripple-animation.component.css
================================================
@keyframes circle {
from {
transform: scale(0);
}
to {
transform: scale(0.5);
}
}
.circle {
margin: 0 auto;
width: 25vw;
height: 25vw;
border: 1vw solid rgba(255, 89, 75, 0.1);
border-radius: 50%;
position: absolute;
left: 0;
top: 0;
}
.one {
animation: circle 4s infinite linear;
}
.two {
animation: circle 3s infinite linear;
}
.three {
animation: circle 2s infinite linear;
}
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/ripple-animation/codelab-ripple-animation.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/ripple-animation/codelab-ripple-animation.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CodelabRippleAnimationComponent } from './codelab-ripple-animation.component';
describe('CodelabRippleAnimationComponent', () => {
let component: CodelabRippleAnimationComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CodelabRippleAnimationComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CodelabRippleAnimationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/ripple-animation/codelab-ripple-animation.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'codelab-ripple-animation',
templateUrl: './codelab-ripple-animation.component.html',
styleUrls: ['./codelab-ripple-animation.component.css']
})
export class CodelabRippleAnimationComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/title-slide.component.css
================================================
:host {
height: 100%;
}
.title-slide {
height: 100%;
display: flex;
flex-direction: column;
}
.header {
flex: 0.5;
display: flex;
justify-content: flex-end;
flex-direction: column;
background: #982a2a;
color: white;
font-size: 10vw;
padding: 2vw;
}
.contents {
padding: 2vw;
display: flex;
flex-direction: column;
flex: 0.5;
}
.description {
font-size: 5vw;
}
.instructions {
flex-grow: 1;
font-size: 4vw;
color: #666;
margin-top: 20px;
}
.left-half-ripple {
overflow: hidden;
margin: 0;
height: 25vw;
width: calc(25vw / 2 + 2vw);
position: absolute;
top: calc(50% - 25vw / 2);
right: 0;
}
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/title-slide.component.html
================================================
{{ description }}
Use ← and → on your keyboard to navigate the slides.
Prerequisites: {{ prereqs }}
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/title-slide.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { TitleSlideComponent } from './title-slide.component';
import { CodelabRippleAnimationComponent } from './ripple-animation/codelab-ripple-animation.component';
describe('TitleSlideComponent', () => {
let component: TitleSlideComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CodelabRippleAnimationComponent, TitleSlideComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TitleSlideComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should render a title', () => {
component.title = 'awesome title';
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.header'));
expect(el.nativeElement.textContent).toContain('awesome title');
});
it('should render a description', () => {
component.description = 'hello world!';
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.description'));
expect(el.nativeElement.textContent).toContain('hello world!');
});
it('should render prerequisites', () => {
component.prereqs = 'do this first';
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.prereqs'));
expect(el.nativeElement.textContent).toContain('do this first');
expect(el.nativeElement.textContent).toContain('Prerequisites');
});
it('should not show "Prerequisites" if undefined', () => {
component.prereqs = undefined;
fixture.detectChanges();
const el = fixture.debugElement.query(By.css('.prereqs'));
expect(el.nativeElement.textContent).not.toContain('Prerequisites');
});
});
================================================
FILE: apps/codelab/src/app/components/slides/title-slide/title-slide.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'codelab-title-slide',
templateUrl: './title-slide.component.html',
styleUrls: ['./title-slide.component.css']
})
export class TitleSlideComponent {
@Input() title: string;
@Input() description: string;
@Input() prereqs: string;
}
================================================
FILE: apps/codelab/src/app/components/slides-preview/codelab-preview.component.html
================================================
================================================
FILE: apps/codelab/src/app/components/slides-preview/codelab-preview.component.scss
================================================
.codelab-preview {
position: relative;
width: 100%;
height: 100%;
}
.codelab-preview.expanded {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
background-color: #fff;
}
.preview-header {
position: absolute;
right: 1px;
top: 1px;
}
.button-expand {
outline: none;
width: 35px;
height: 35px;
padding: 0;
border: 0;
background-image: url('/assets/images/expand.png');
background-color: #fff;
background-size: 100% 100%;
cursor: pointer;
&:hover {
background-color: #eee;
}
}
iframe {
box-shadow: 0 0 1px #dddddd;
border: 0 solid;
}
.body,
iframe {
width: 100%;
height: 100%;
}
================================================
FILE: apps/codelab/src/app/components/slides-preview/codelab-preview.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { Location } from '@angular/common';
@Component({
selector: 'codelab-preview',
templateUrl: './codelab-preview.component.html',
styleUrls: ['./codelab-preview.component.scss']
})
export class CodelabPreviewComponent implements OnInit {
@Input() milestone;
public isExpanded: boolean;
private iframeSource;
constructor(
private sanitizer: DomSanitizer,
private router: Router,
private activatedRoute: ActivatedRoute,
private location: Location
) {}
ngOnInit() {
const url = this.location.prepareExternalUrl(
this.router
.createUrlTree(['.'], {
relativeTo: this.activatedRoute,
queryParams: {
milestone: this.milestone,
hideControls: true,
resize: true
},
queryParamsHandling: 'merge'
})
.toString()
);
this.iframeSource = this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
expandToggle() {
return (this.isExpanded = !this.isExpanded);
}
}
================================================
FILE: apps/codelab/src/app/containers/full-layout/full-layout.component.html
================================================
================================================
FILE: apps/codelab/src/app/containers/full-layout/full-layout.component.scss
================================================
================================================
FILE: apps/codelab/src/app/containers/full-layout/full-layout.component.ts
================================================
import { ChangeDetectionStrategy, Component, Optional } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'codelab-full-layout',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './full-layout.component.html',
styleUrls: ['./full-layout.component.scss']
})
export class FullLayoutComponent {
displayButtons: boolean;
constructor(@Optional() route: ActivatedRoute) {
this.displayButtons = !route.snapshot.queryParams.milestone;
}
}
================================================
FILE: apps/codelab/src/app/containers/full-layout/full-layout.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FullLayoutComponent } from './full-layout.component';
import { SyncModule } from '../../sync/sync.module';
import { ButtonsNavBarModule } from '../../components/buttons-nav-bar/buttons-nav-bar.module';
@NgModule({
imports: [RouterModule, SyncModule, ButtonsNavBarModule],
declarations: [FullLayoutComponent]
})
export class FullLayoutModule {}
================================================
FILE: apps/codelab/src/app/containers/full-layout/index.ts
================================================
export * from './full-layout.component';
================================================
FILE: apps/codelab/src/app/containers/index.ts
================================================
export * from './full-layout';
================================================
FILE: apps/codelab/src/app/directives/directives.module.ts
================================================
import { NgModule } from '@angular/core';
import { IsLoggedInDirective } from './permissions/is-logged-in/is-loggef-in.directive';
import { CanLoadAdminDirective } from './permissions/can-load-admin/can-load-admin.directive';
import { NextSlideDirective } from './nextSlide.directive';
import { PreviousSlideDirective } from './previousSlide.directive';
@NgModule({
declarations: [
IsLoggedInDirective,
CanLoadAdminDirective,
NextSlideDirective,
PreviousSlideDirective
],
exports: [
IsLoggedInDirective,
CanLoadAdminDirective,
NextSlideDirective,
PreviousSlideDirective
]
})
export class DirectivesModule {}
================================================
FILE: apps/codelab/src/app/directives/nextSlide.directive.ts
================================================
import { Directive, HostListener } from '@angular/core';
import { SlidesDeckComponent } from '@ng360/slides';
@Directive({
selector: '[nextSlide]'
})
export class NextSlideDirective {
constructor(public deck: SlidesDeckComponent) {}
@HostListener('click')
onClick() {
this.deck.nextSlide();
}
}
================================================
FILE: apps/codelab/src/app/directives/permissions/abstract-permission.ts
================================================
import {
TemplateRef,
ViewContainerRef,
OnDestroy,
Injectable
} from '@angular/core';
import { AccessService } from '../../shared/services/access.service';
import { ReplaySubject, Observable } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@Injectable()
export class AbstractPermission implements OnDestroy {
public destroy = new ReplaySubject(1);
constructor(
public templateRef: TemplateRef,
public viewContainer: ViewContainerRef,
public accessService: AccessService
) {}
ngOnDestroy() {
this.destroy.next(null);
this.destroy.complete();
}
public render(observable: Observable): void {
observable.pipe(takeUntil(this.destroy)).subscribe(hasPermission => {
if (hasPermission) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.clear();
}
});
}
}
================================================
FILE: apps/codelab/src/app/directives/permissions/can-load-admin/can-load-admin.directive.ts
================================================
import {
Directive,
OnInit,
TemplateRef,
ViewContainerRef
} from '@angular/core';
import {
AccessService,
Permissions
} from '../../../shared/services/access.service';
import { AbstractPermission } from '../abstract-permission';
@Directive({
selector: '[canLoadAdmin]'
})
export class CanLoadAdminDirective extends AbstractPermission
implements OnInit {
constructor(
templateRef: TemplateRef,
viewContainer: ViewContainerRef,
accessService: AccessService
) {
super(templateRef, viewContainer, accessService);
}
ngOnInit() {
this.render(this.accessService.can(Permissions.CAN_LOAD_ADMIN));
}
}
================================================
FILE: apps/codelab/src/app/directives/permissions/is-logged-in/is-loggef-in.directive.ts
================================================
import {
Directive,
OnInit,
TemplateRef,
ViewContainerRef
} from '@angular/core';
import { AccessService } from '../../../shared/services/access.service';
import { AbstractPermission } from '../abstract-permission';
@Directive({
selector: '[isLoggedIn]'
})
export class IsLoggedInDirective extends AbstractPermission implements OnInit {
constructor(
templateRef: TemplateRef,
viewContainer: ViewContainerRef,
accessService: AccessService
) {
super(templateRef, viewContainer, accessService);
}
ngOnInit() {
this.render(this.accessService.oldIsAdmin$);
}
}
================================================
FILE: apps/codelab/src/app/directives/previousSlide.directive.ts
================================================
import { Directive, HostListener } from '@angular/core';
import { SlidesDeckComponent } from '@ng360/slides';
@Directive({
selector: '[previousSlide]'
})
export class PreviousSlideDirective {
constructor(public deck: SlidesDeckComponent) {}
@HostListener('click')
onClick() {
this.deck.previousSlide();
}
}
================================================
FILE: apps/codelab/src/app/shared/angular-code/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: 'Hi!'
})
export class AppComponent {}
================================================
FILE: apps/codelab/src/app/shared/angular-code/app.module.ts
================================================
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/codelab/src/app/shared/angular-code/bootstrap.ts
================================================
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { ResourceLoader } from '@angular/compiler';
import * as code from './code';
import { AppModule } from './app.module';
class MyResourceLoader extends ResourceLoader {
get(url: string): Promise {
const templateId = Object.keys(code).find(key =>
key.includes(url.replace(/[\/\.-]/gi, '_'))
);
const template = code[templateId];
if (!template) {
console.log(template);
// tslint:disable-next-line:no-debugger
debugger;
}
return Promise.resolve(template);
}
}
platformBrowserDynamic().bootstrapModule(AppModule, [
{
providers: [
{
provide: ResourceLoader,
useFactory: () => new MyResourceLoader(),
deps: []
}
]
}
]);
================================================
FILE: apps/codelab/src/app/shared/angular-code/code.ts
================================================
export const code = '';
================================================
FILE: apps/codelab/src/app/shared/angular-code/index.html
================================================
Loading...
================================================
FILE: apps/codelab/src/app/shared/helpers/codelabFile.ts
================================================
import { FileConfig } from '../interfaces/file-config';
export enum FileType {
TypeScript = 'typescript',
Html = 'html',
Css = 'css'
}
const fileConfig = {
[FileType.TypeScript]: {
extension: '.ts'
},
[FileType.Html]: {
extension: '.html'
},
[FileType.Css]: {
extension: '.css'
}
};
export class CodelabFile implements FileConfig {
highlight?: RegExp | RegExp[];
after: string;
public code: string;
public template: string;
public solution: string;
public bootstrap = false;
public path: string;
public excludeFromTesting = false;
public test = false;
public before: string;
public hidden = false;
static TypeScriptFile(name: string): CodelabFile {
return new CodelabFile(FileType.TypeScript, name).setAfter(
'export function evalJs( js ){ return eval(js);}'
);
}
static TypeScriptTest(name: string): CodelabFile {
return new CodelabFile(FileType.TypeScript, name + 'Test').makeTest();
}
static Html(name: string): CodelabFile {
return new CodelabFile(FileType.Html, name);
}
static Css(name: string): CodelabFile {
return new CodelabFile(FileType.Css, name);
}
constructor(
public readonly type: FileType,
public readonly moduleName: string
) {
this.path = moduleName + fileConfig[type].extension;
}
public setAfter(after: string): CodelabFile {
this.after = after;
return this;
}
public clone(): CodelabFile {
return Object.assign(Object.create(CodelabFile), this);
}
public makeBootstrappable(): CodelabFile {
this.bootstrap = true;
this.excludeFromTesting = true;
return this;
}
public makeHidden(): CodelabFile {
this.hidden = true;
return this;
}
public makeTest(): CodelabFile {
this.test = true;
this.before = 'mochaBefore();';
this.after = 'mochaAfter();';
this.excludeFromTesting = false;
this.makeHidden();
this.makeBootstrappable();
return this;
}
public setSolution(solution: string): CodelabFile {
this.solution = solution;
return this;
}
public withHighlight(highlight: RegExp | RegExp[]) {
this.highlight = highlight;
return this;
}
public setCode(code: string): CodelabFile {
this.code = code;
this.template = code;
if (!this.solution) {
this.solution = code;
}
return this;
}
}
================================================
FILE: apps/codelab/src/app/shared/helpers/helpers.ts
================================================
import { FileConfig } from '../interfaces/file-config';
function exerciseWithConsoleLog(moduleName: string, code: any, code2: any) {
return {
...exercise(moduleName, code, code2),
before: `
export const value = window.value = window.value || {};
function wrap(context, prop, callback){
if(!context[prop].patched){
const originalMethod = context[prop];
context[prop] = function(...args){
callback(...args);
return originalMethod.apply(context, args);
}
context[prop].patched = true;
}
}
/* TODO: Get rid of the CSS hack */
wrap(console, 'log', (v)=>{
value.value = v;
document.body.innerHTML += '' +
'< ' + JSON.stringify(v, null, ' ') + ' ' +
' '
document.body.scrollTop = document.body.scrollHeight;
})
`
};
}
export function exercise(
moduleName: string,
template: string,
solution?: string
): FileConfig {
solution = solution || template;
return {
bootstrap: false,
excludeFromTesting: false,
type: 'typescript',
path: moduleName + '.ts',
template,
code: template,
moduleName: moduleName,
solution,
after: `export function evalJs( js ){ return eval(js);}`
};
}
export function test(moduleName: string, template: string): FileConfig {
return {
path: moduleName + 'Test.ts',
type: 'typescript',
template,
code: template,
moduleName: moduleName + 'Test',
excludeFromTesting: false,
test: true,
bootstrap: true,
before: 'mochaBefore();',
after: 'mochaAfter();',
hidden: true
};
}
interface SimpleImport {
name: string;
path: string;
}
export const builder = {
imports(imports: Array) {
return imports.map(i => `import {${i.name}} from '${i.path}';`).join('\n');
},
listOfComponents: (components: Array) => {
return `[${components.map(c => c.name).join(',')}]`;
},
ngModule(
declarations: Array = [
{
name: 'AppComponent',
path: './app.component'
}
],
bootstrapComponent?: Array
) {
bootstrapComponent = bootstrapComponent || declarations;
return `import {BrowserModule} from \'@angular/platform-browser\';
import {NgModule} from \'@angular/core\';
${this.imports(declarations)}
@NgModule({
imports: [BrowserModule],
declarations: ${this.listOfComponents(declarations)},
bootstrap: ${this.listOfComponents(bootstrapComponent)}
})
export class AppModule {}`;
},
bootstrap(
module: SimpleImport = { name: 'AppModule', path: './app.module' }
): string {
return `import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
${this.imports([module])}
import {ResourceLoader} from '@angular/compiler';
class MyResourceLoader extends ResourceLoader {
get(url: string): Promise {
const templateId = Object.keys(code).find(key => key.includes(url.replace(/[\\/\\.-]/gi, '_')));
const template = code[templateId];
if (!template) {
console.log(template);
// tslint:disable-next-line:no-debugger
debugger;
}
return Promise.resolve(template);
};
}
const platform = platformBrowserDynamic();
platform.bootstrapModule(${module.name}, [
{
providers: [
{provide: ResourceLoader, useFactory: () => new MyResourceLoader(), deps: []}
]
}
]);
`;
}
};
export function html(path = 'app', code, solution = '') {
return {
code,
path: path + '.html',
solution: solution,
type: 'html'
};
}
export function stylesheet(code, solution = '') {
return {
code,
path: 'style.css',
solution: solution || code,
type: 'css'
};
}
export function bootstrap(
moduleName: string,
template: string,
solution?: string
) {
solution = solution || template;
return {
bootstrap: true,
excludeFromTesting: true,
type: 'typescript',
path: moduleName + '.ts',
template,
code: template,
moduleName: moduleName,
solution
};
}
export function circleAndBox() {
const result = boxAndCircle();
const temp = result.files[0];
result.files[0] = result.files[1];
result.files[1] = temp;
return result;
}
// That's me being plain lazy, we need
export function boxAndCircle() {
const moduleCode = `import {BrowserModule} from \'@angular/platform-browser\';
import {NgModule} from '@angular/core';
import {BoxComponent} from './box.component';
import {CircleComponent} from './circle.component';
@NgModule({
imports: [BrowserModule],
declarations: [CircleComponent, BoxComponent],
bootstrap: [BoxComponent]
})
export class AppModule {}`;
const circleCode = `import { Component, Input } from '@angular/core';
@Component({
selector: 'slides-circle',
template: '
'
})
export class CircleComponent {
@Input() size: number;
@Input() color: string;
}`;
const boxCode = `import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: \`
\`
})
export class BoxComponent {
circleColor="green"
}`;
const bootstrapCode = `import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
`;
return {
other: {
boxNoParams: `import { Component } from '@angular/core';
@Component({
selector: 'slides-box',
template: \`
\`
})
export class BoxComponent {
circleColor="green"
}`
},
files: [
exercise('box.component', boxCode),
exercise('circle.component', circleCode),
exercise('app.module', moduleCode),
bootstrap('main', bootstrapCode, bootstrapCode),
{
type: 'css',
path: 'styles.css',
template: `
my-app > div {
width: 300px;
height: 200px;
border: 1px #ddd solid;
}
.circle {
margin-left: 100px;
margin-top: 50px;
border-radius: 50%;
}
`
}
]
};
}
export function displayAngularComponent(
componentCode: string,
testCode?: string
) {
// tslint:disable-next-line:max-line-length TODO: Clean up next line and remove this comment.
const moduleCode = builder.ngModule();
const bootstrapCode = builder.bootstrap();
return {
files: [
exercise('app.component', componentCode),
exercise('app.module', moduleCode),
bootstrap('main', bootstrapCode, bootstrapCode),
{
type: 'css',
path: 'styles.css',
code: `
body, html {
margin: 0;
padding: 2vw;
font-family: sans-serif;
}
h1, h2 {
margin: 0;
padding: 0;
}
h1 {font-size: 6vw;}
h2 {font-size: 4vw;}
`
},
...(testCode ? [test('test', testCode)] : [])
]
};
}
export function typeScriptWithConsoleLog(
code: string,
bootstrapCode = 'import "./app";',
testCode = '',
otherCode = ''
) {
const files = [
exerciseWithConsoleLog('app', code, code),
bootstrap('main', bootstrapCode, bootstrapCode),
test('test', testCode),
{
path: 'main.css',
type: 'css',
code: ``
}
];
if (otherCode !== '') {
files.push(exercise('puppy', otherCode));
}
return {
files
};
}
export function javaScriptWithConsoleLog(
code: string,
bootstrapCode = 'import "./app";',
testCode = '',
otherCode = ''
) {
const result = typeScriptWithConsoleLog(
code,
bootstrapCode,
testCode,
otherCode
);
(result.files[0] as FileConfig).editorType = 'javascript';
return result;
}
export function displayAngularComponentWithHtml(
componentCode: string,
code: string
) {
return {
files: [
{
code,
path: 'app/app.html',
solution: '',
type: 'html'
},
...displayAngularComponent(componentCode).files
]
};
}
export function solve(exerciseConfig) {
return {
...exerciseConfig,
files: exerciseConfig.files.map(file => ({
...file,
code: file.solution || file.code
}))
};
}
================================================
FILE: apps/codelab/src/app/shared/interfaces/exercise-config.ts
================================================
import { FileConfig } from './file-config';
import { TestInfo } from './test-info';
export interface ExerciseConfig {
name: string;
description: string;
runner?: string;
files: Array;
skipTests?: boolean;
tests?: Array;
}
================================================
FILE: apps/codelab/src/app/shared/interfaces/file-config.ts
================================================
export interface FileConfig {
opened?: boolean;
/**
* typescript or html.
*/
type?: string;
/**
* Source code of the file.
*/
code?: string;
template: string;
/**
* Source code of the file.
*/
solution?: string;
/**
* TS code to run before running the file.
*/
before?: string;
/**
* TS code to run after running the file.
*/
after?: string;
/**
* Usually the same as fileName without .ts postfix.
* Currently gets inferred from filename.
*/
moduleName?: string;
/**
* Actual filename.
*/
path: string;
/**
* If this is true; the file will be included in the preview iframe.
*/
ui?: boolean;
/**
* If this is true
*/
bootstrap?: boolean;
excludeFromTesting?: boolean;
/**
* if this is true; the file will be displayed in read only mode.
*/
readonly?: boolean;
/**
* If this is true the file will be included in the test iframe.
*/
test?: boolean;
/**
* If this is true; the file will be hidden.
*/
hidden?: boolean;
/**
* File dependencies, need for proper highlighting in monaco.
*/
editorType?: string;
// If this is set, this will be executed as a test.
// This is a hack and will be removed.
execute?: any;
}
================================================
FILE: apps/codelab/src/app/shared/interfaces/test-info.ts
================================================
import { FileConfig } from './file-config';
export interface TestInfo {
title: string;
file: FileConfig;
pass?: boolean;
result?: string;
filename?: string;
}
================================================
FILE: apps/codelab/src/app/shared/services/access.service.ts
================================================
import { Injectable } from '@angular/core';
import { LoginService } from '@codelab/firebase-login';
import { SyncDbService } from '@codelab/utils/src/lib/sync/services/sync-db.service';
import { filter, switchMap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { SyncDb } from '@codelab/utils/src/lib/sync/services/sync-data.service';
export enum Permissions {
MANAGE_USERS = 'manage_users',
CAN_LOAD_ADMIN = 'can_load_admin'
}
@Injectable({ providedIn: 'root' })
export class AccessService {
readonly oldIsAdmin$ = this.loginService.uid$.pipe(
switchMap(uid => {
return this.dbService
.object('authorized_users')
.object(uid)
.withDefault(false)
.valueChanges();
})
);
private readonly adminPermissions = this.dbService
.object('admin')
.object(this.loginService.uid$)
.object('permissions');
constructor(
private readonly loginService: LoginService,
private readonly dbService: SyncDbService
) {}
can(p: Permissions): Observable {
return (
this.adminPermissions
// TODO(kirjs): default: false
.object(p)
.valueChanges()
.pipe(filter(a => a !== null))
);
}
}
================================================
FILE: apps/codelab/src/app/shared/services/guards/admin-guard.ts
================================================
import { Injectable } from '@angular/core';
import {
Router,
RouterStateSnapshot,
ActivatedRouteSnapshot,
CanActivate
} from '@angular/router';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AccessService, Permissions } from '../access.service';
@Injectable({ providedIn: 'root' })
export class AdminGuard implements CanActivate {
constructor(private _route: Router, private accessService: AccessService) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable | boolean {
return true;
return this.accessService.can(Permissions.CAN_LOAD_ADMIN).pipe(
map(hasAccess => {
if (!hasAccess) {
this._route.navigate(['login']);
return false;
}
return true;
})
);
}
}
================================================
FILE: apps/codelab/src/app/shared/services/guards/login-guard.ts
================================================
import { Injectable } from '@angular/core';
import { Router, RouterStateSnapshot, CanActivate } from '@angular/router';
import { LoginService } from '@codelab/firebase-login';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class LoginGuard implements CanActivate {
constructor(private _route: Router, private loginService: LoginService) {}
canActivate(
ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable | boolean {
return this.loginService.isAnonymous$.pipe(
map(res => {
if (res) {
// user is anonymous
return true;
}
// user is logged in, navigate to home
this._route.navigate(['/']);
return false;
})
);
}
}
================================================
FILE: apps/codelab/src/app/shared/shared.module.ts
================================================
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { SlidesModule } from '@ng360/slides';
import { CodeDemoModule } from '@codelab/code-demos';
import { CodelabComponentsModule } from '../components/codelab-components.module';
import { ButtonsNavBarModule } from '../components/buttons-nav-bar/buttons-nav-bar.module';
import { SyncModule } from '../sync/sync.module';
@NgModule({
imports: [
HttpClientModule,
FormsModule,
RouterModule,
CodeDemoModule,
CodelabComponentsModule,
SlidesModule,
ButtonsNavBarModule,
SyncModule
],
exports: [
HttpClientModule,
FormsModule,
CodeDemoModule,
CodelabComponentsModule,
SlidesModule,
ButtonsNavBarModule
]
})
export class SharedModule {}
================================================
FILE: apps/codelab/src/app/sync/sync.component.css
================================================
.survey {
opacity: 0.98;
position: absolute;
top: 20px;
left: 20px;
bottom: 20px;
right: 20px;
border: 1px solid #000;
background-color: #fff;
padding: 20px;
z-index: 1000;
font-family: sans-serif;
}
h1 {
text-align: center;
}
================================================
FILE: apps/codelab/src/app/sync/sync.component.html
================================================
{{ userId$ | async }}
================================================
FILE: apps/codelab/src/app/sync/sync.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SyncComponent } from './sync.component';
import { SyncModule } from './sync.module';
import { AngularFireDatabase } from '@angular/fire/database';
import { AngularFireAuth } from '@angular/fire/auth';
import {
getMockAngularFireProviders,
MockAngularFireAuth,
MockAngularFireDatabase
} from '@codelab/utils/src/lib/testing/mocks/angular-fire';
describe('SyncComponent', () => {
let component: SyncComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [SyncModule],
providers: [getMockAngularFireProviders()]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SyncComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/codelab/src/app/sync/sync.component.ts
================================================
import { Component, Input } from '@angular/core';
import { SyncPollConfig } from '@codelab/utils/src/lib/sync/components/poll/common/common';
import { SyncDataService } from '@codelab/utils/src/lib/sync/services/sync-data.service';
import { SyncSessionService } from '@codelab/utils/src/lib/sync/services/sync-session.service';
import { switchMap } from 'rxjs/operators';
import {
canWritePresenterData,
SyncStatus
} from '@codelab/utils/src/lib/sync/common';
import { of } from 'rxjs';
@Component({
selector: 'codelab-sync-survey',
templateUrl: './sync.component.html',
styleUrls: ['./sync.component.css']
})
export class SyncComponent {
@Input() admin = false;
readonly userId$ = this.syncSessionService.viewerId$;
readonly isPresentationEnabled$ = this.syncDataService
.getPresenterObject('enabled')
.withDefault(false)
.valueChanges();
readonly shouldShowPresentation$ = this.syncSessionService.status$.pipe(
switchMap(s => {
if (canWritePresenterData(s)) {
return of(true);
}
if (s === SyncStatus.OFF) {
return this.admin
? this.syncSessionService.canStartSession$
: of(false);
}
return this.isPresentationEnabled$;
})
);
polls: SyncPollConfig[] = [
{
key: 'js',
type: 'stars',
question: 'How well do you know JavaScript'
},
{
key: 'ts',
type: 'stars',
question: 'How well do you know TypeScript'
},
{
key: 'angularjs',
type: 'stars',
question: 'How well do you know AngularJS (Old version)'
},
{
key: 'angular',
type: 'stars',
question:
'How well do you know Angular (The new version we are learning today)'
}
];
constructor(
private readonly syncDataService: SyncDataService,
private readonly syncSessionService: SyncSessionService
) {}
}
================================================
FILE: apps/codelab/src/app/sync/sync.module.ts
================================================
import { Component, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { SyncPollModule } from '@codelab/utils/src/lib/sync/components/poll/sync-poll.module';
import { SyncButtonModule } from '@codelab/utils/src/lib/sync/sync-button/sync-button.module';
import { SyncDirectivesModule } from '@codelab/utils/src/lib/sync/directives/sync-directives.module';
import { ConfigureSyncModule } from '@codelab/utils/src/lib/sync/components/configure-sync/configure-sync.module';
import { SyncComponent } from './sync.component';
@NgModule({
declarations: [SyncComponent],
exports: [SyncComponent],
imports: [
CommonModule,
SlidesModule,
SyncPollModule,
SyncButtonModule,
SyncDirectivesModule,
ConfigureSyncModule
]
})
export class SyncModule {}
@Component({
selector: 'codelab-sync-admin-wrapper',
template: ' '
})
export class SyncAdminWrapperComponent {}
const routes = RouterModule.forChild(
SlidesRoutes.get(SyncAdminWrapperComponent)
);
@NgModule({
declarations: [SyncAdminWrapperComponent],
imports: [SyncModule, routes]
})
export class SyncAdminModule {}
================================================
FILE: apps/codelab/src/assets/.gitkeep
================================================
================================================
FILE: apps/codelab/src/environments/environment.prod.ts
================================================
export const environment = {
production: true,
// Firebase
// TODO: Create a new firebase app/config for prod deploy.
firebaseConfig: {
apiKey: 'AIzaSyBiY1Lg2RIcKtbgqzfE6Vrg28Zjal6ZWHs',
authDomain: 'angular-presentation.firebaseapp.com',
databaseURL: 'https://angular-presentation.firebaseio.com',
projectId: 'angular-presentation',
storageBucket: 'angular-presentation.appspot.com',
messagingSenderId: '1087862173437',
appId: '1:1087862173437:web:0bb7fe324b62580bb31894'
}
};
================================================
FILE: apps/codelab/src/environments/environment.ts
================================================
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
production: false,
// Firebase
firebaseConfig: {
apiKey: 'AIzaSyBiY1Lg2RIcKtbgqzfE6Vrg28Zjal6ZWHs',
authDomain: 'angular-presentation.firebaseapp.com',
databaseURL: 'https://angular-presentation.firebaseio.com',
projectId: 'angular-presentation',
storageBucket: 'angular-presentation.appspot.com',
messagingSenderId: '1087862173437',
appId: '1:1087862173437:web:0bb7fe324b62580bb31894'
}
};
================================================
FILE: apps/codelab/src/index.html
================================================
Angular Codelab
Please enable JavaScript to continue using this application.
================================================
FILE: apps/codelab/src/locale/codelab.ru.xtb
================================================
]>
Angular Codelab
Милый котенок
В следующем упражнении вы будете использовать созданный ранее компонент
Здесь перечислены документация, описание функций и событий
Предположим, имеется HTML файл:
Это может быть сделано в три этапа.
Три этапа для создания приложения:
Создание Angular компонента
Создание Angular модуля
Запуск (Bootstrap) модуля
Компонент определяется декоратором <b> @Component</b> у класса
Создайте первый Angular компонент!
порядок загрузки: index -> main -> app.module -> app.component
Конец раздела загрузки (Bootstrap)
Отлично сработано! Упражнение выполнено!
Внедрение зависимостей
Узнайте про систему внедрения зависимости в Angular
Отметить зависимость декоратором @Injectable()
Передать модулю
Запросить в компоненте
Запрашиваем внедряемый сервис в компоненте
При привязке можно использовать произвольные выражения
Почему TypeScript?
TypeScript
В TypeScript есть<b> классы</b> , и Angular часто их использует.
Теперь мы можем использовать класс <b> Puppy</b> в другом файле.
Интерфейсы
Перечисляемые типы (enums)
Асинхронность / Ожидания
Деструктурирование
И много чего еще!
На следующем слайде будет упражнение по TypeScript
Выглядеть будет вот так:
Или пользуйтесь сокращенной натацией
Ошибка: очевидно, что это не собачка
Это число (number)
Дерево компонентов
Родительские и дочерние компоненты
Родительские и дочерние компоненты
Обзор
Упражнение 2
@Component это декоратор Angular
Декоратор указывается над сущностью (классом)
Имя компонента это имя класса (AppComponent)
Что такое Angular
Селектор
Встроенный шаблон
Модуль
Декоратор NgModule
BrowserModule
Declarations
Bootstrap
Создание первого Ng модуля
Bootstrapping
Bootstrapping 1
Bootstrapping 2
Bootstrapping 3
*Сокращения в Typescript делают 'profession' доступным в объекте компонента
предполагая, что Job имеет свойство '.title'
Параметры
Тестирование
Пример
Это рабочий HTML синтаксис.
Так можно привязать значение аттрибутов
Это позволяет условно сделать привязку к классу
Или CSS
Все работает с пользовательскими компонентами
При нажатии на кнопку, вызовется метод компонента "saveUser" и передаст соответствующий event.
Вы также можете создавать события для собственных компонентов.
Здесь у нас есть событие "depleted", которое вызовет "soundAlarm" когда сработает
Еще есть упрощенный способ привязки событий!
Если пользователь нажмет CTRL+ENTER, запустится метод submit (это возможность Angular)
в userName есть ссылка на input
Попробуйте изменить на true!
Необходимо повторить собачку тут
Интерполирование
Cвойства
Привязка свойства
Продвинутая привязка данных
Привязка событий
Повторяющиеся элементы
Повторение элементов (*ngFor)
Упражнение 3
Или используйте сокращенную нотацию для функций
(стрелочные функции)
TypeScript догадается, что это число
TypeScript догадается, что это строка (string)
Невозможно сложить число с логическим значением
У прототипа Number нет метода slice
Но можно для строки
Работает!
Type[] делает тоже самое.
Это метод
Гав гав
Теперь мы можем создать экземпляр класса Puppy
И вызвать его методы
Потом добавим здесь код
Создадим еще собачек
Var по-прежнему разрешен, но не рекомендуется.
Let рекомендуется для использования взамен var.
В отличие от var let недоступен вне this.
Const похож на let, но если вы попробуете изменить его, то TS выдаст ошибку.
хорошо, определенно boolean
TypeScript
Массивы
Конструктор
Модификатор доступа
Экспорт
Импорт
Фильтр (последнее)
Еще...
Раздел завершен
Тут не должно быть точки с запятой. Декоратор привязывается к классу.
Первое приложение на Angular
Вы узнаете как создать Angular компонент, добавить его в модуль и запустить приложение.
Знание основ TypeScript
Шаблоны
Узнайте больше о шаблонах в Angular!
Создайте класс с именем 'Codelab'
Основы JavaScript.
video/video.component.ts: Отметьте компонент декоратором '@Component' и добавьте в него селектор 'my-video'.
app.module.ts: Объявите компонент VideoComponent в свойстве 'declarations' модуля AppModule.
video.component.ts Добавьте ссылку на соответствующий HTML файл с шаблоном в свойство templateUrl , чтобы загрузить шаблон в компонент
video/video.component.ts: Добавьте свойство video и отметьте его декоратором @Input()
video/video.component.html: Отобразите название (title) видео
video/video.component.html: Отобразите превью (src) видео
video/video.component.html: Отобразите описание(description) видео
video/video.component.html: Выведите дату видео
video/video.component.html: Отобразите количество просмотров видео(views)
video/video.component.html: Отобразите количество отметок "нравится"(likes)
app.html: Замените текущие название и превью видео на наш чудесный компонент <my-video>
app.html: С помощью биндинга передайте объект "video" в компонент my-video (не забудьте про квадратные скобки)
Создайте класс 'AppComponent'
Создайте класс 'AppModule'
Всё готово! Загружайте приложение
Экспортируйте класс
Добавьте классу декоратор @Component
Добавьте селектор 'my-app' в @Component декоратор компонента
Добавьте шаблон, содержащий тег h1 с текстом "Hello MewTube!"
Добавьте классу декоратор NgModule
Добавьте 'BrowserModule' в свойство 'imports' декоратора NgModule
Добавьте компонент 'AppComponent' к свойству 'declarations' у декоратора
Добавьте компонент 'AppComponent' в свойство 'bootstrap' декоратора
video.service.ts: Добавьте классу декоратор @Injectable()
app.module.ts: Добавьте VideoService в свойство providers вашего NgModule
app.component.ts: Избавьтесь от константы FAKE_VIDEOS
app.component.ts: Внедрите 'VideoService' в конструктор компонента как
'videoService'
app.component.ts: Сделайте так, чтобы метод 'search' компонента использовал метод 'search' сервиса 'videoService'
app.component.ts: Добавьте свойство 'videos', и установите пустой массив в качестве значения.
app.component.ts: Внутри метода 'search' установите переменную FAKE_VIDEOS в качестве значения свойства 'videos'.
app.component.ts: Добавьте в компонент метод 'search', который принимает параметр 'searchString'
app.html: Добавьте обработчик нажатия мышки на кнопку, вызовите метод 'search' и передайте значение поля ввода (Сам поиск мы сделаем позже)
app.html: Добавьте сообщение 'no videos', которое появляется только когда массив videos пуст
#Bonus app.component.ts: Сейчас вам придется нажать кнопку поиска, чтобы отобразились видео. Поправьте код так, чтобы видео отображались по умолчанию, без нажатия кнопки.
app.component.ts: Внутри метода 'search' отфильтруйте FAKE_VIDEOS так, чтобы возвращались только видео, название которых содержит искомую строку searchString. (подсказка: используйте .includes или .indexOf строковый метод)
app.html: Отобразите превью
app.html: Пройдитесь по массиву videos с помощью '*ngFor' и выведите название каждого видео
app.html: Добавьте кнопку (<button>) с текстом 'search' (Поиск)
app.html: Добавьте тег input с атрибутом 'placeholder' = 'video'
Добавьте конструктор
Примите значение 'guests' в качестве параметра конструктора
Укажите тип параметра guests (подсказка: массив типа Guest)
Отметьте параметр ключевым словом public (обратите внимание, что теперь вы можете получить к нему доступ в классе, обратившись к this.guests)
Создайте метод 'getGuestsComing'
Измените метод getGuestsComing так, чтобы он возвращал массив из элементов 'guests', у которых свойство 'coming' равно true.
Создайте ваше первое Angular приложение
Пользовательские события
Узнайте как создать и запустить ваше первое Angular приложение
Узнайте как пользоваться шаблонами в Angular
Узнайте как использовать систему внедрения звисимостей вместо того, чтобы хардкодить их
Узнайте как лучше всего организовать ваше приложение с помощью пользовательских компонентов.
Узнайте как привязывать события
Следующий шаг - определить компонент в <b> <b> NgModule</b> </b> .
С Angular мы создаем мобильные приложения, используя NativeScript или Ionic
С Angular мы можем создать VR приложения с помощью A-FRAME или WEDGL
Конец раздела Формы
Переменная 'b' в коде ниже отмечена как ошибка, так как тип отсутствует. Укажите тип переменной 'b'.
Благодаря этой информации, TypeScript может указать нам на ошибку. Исправьте eё, чтобы 2 + 2 снова стало равно 4!
Другие типы, которые можно использовать
TypeScript
Пропустите, если вы уже знакомы с TypeScript
Angular
Или нажмите сюда чтобы показать все разделы
Это конец курса от Codelab, но только начало вашего путешествия в Angular. Ниже приведены некоторые ссылки, которые могут помочь вам продолжить изучение.
Пока в нашем приложении только один компонент, но по мере роста приложения и добавления новых компонентов, у нас получится дерево компонентов
Внутри компонент может отобразить любой другой с помощью HTML тега, который соответствует селектору выбранного компонента
Родительский компонент передает свои данные дочернему через свойства
Измените <b> <b> размер</b> </b>
на <b> <b> 100 </b> </b> и <b> <b> цвет </b> </b> на <b> <b> красный </b> </b> , чтобы получить японский флаг
Свойства дочернего класса должны быть декорированы специальным <b> <b> @Input() </b> </b> декоратором
В этом случае мы впервые применяем декораторы к свойствам, а не к классам
В первом разделе мы изучили как создавать компоненты. Давайте создадим новый компонент VideoComponent и опишем там информацию, связанную с видео.
Результат будет отображаться автоматически. В итоге должно получиться следующее:
Компоненты не будут знать друг о друге, если они не задекларированы в общем модуле
Angular - это <b> платформа разработки</b> для создания приложений под мобильные телефоны и компьютеры. Angular позволяет <b> расширить HTML синтаксис</b> , чтобы кратко и удобно разрабатывать компоненты приложения. Привязка данных (Data Binding) и внедрение зависимостей (Dependency Injection) позволяют использовать код в существенно меньшем объеме.
Давайте создадим Angular приложение, которое заменит <b> <b> hello-world</b> </b> HTML элемент некоторым наполнением.
Компонент в Angular - это просто класс. Свойства и поведение описываются внутри этого класса.
Декораторы - новая функциональность TypeScript. Они описывают мета-данные к классу, функции, свойству класса или переменной.
'Selector' привязывает компонент к соответствующему элементу в HTML структуре документа. Когда Angular находит <b> <b> hello-world</b> </b> HTML тег в документе, он отрисовывает шаблон HelloWorldComponent'а внутри этого тега
Шаблон (template) определяется HTML кодом, который генерируется компонентом.
Если количество тегов в HTML растет, можно (и рекомендуется) использовать <b> <b> templateUrl</b> </b> вместо этого, указав путь к файлу HTML с шаблоном.
На следующем слайде вы создадите свой первый <b> <b> Angular</b> </b> -компонент!
Подготовьте компонент и опишите его поведение по предложенным инструкциям, результат будет отображен автоматически. В итоге должно получиться следующее:
<b> <b> NgModule</b> </b> не имеет визуального представления и используется исключительно для группировки функциональных элементов Angular
Мы узнаем больше о NgModule в следующих секциях
В списке <b> <b> declarations</b> </b> определяются компоненты, принадлежащие AppModule
Компонент, указываемый в списке <b> <b> bootstrap</b> </b> будет создан и показан в файле <b> <b> index.html</b> </b>
На следующем слайде вы создадите свой первый <b> <b> Angular</b> </b> -модуль.
Подготовьте модуль и опишите его поведение по предложенным инструкциям, результат будет отображен автоматически. В итоге получится следующее:
У нас всё готово, самое время начать (загрузить) приложение
Передайте <b> <b> AppModule</b> </b> в метод <b> <b> bootstrapModule</b> </b> , и он запустит все компоненты из раздела bootstrap этого модуля
Для большинства простых приложений вы можете просто скопировать и вставить код выше «как есть»
Как работает начальная загрузка в Angular?
1. Добавим среду исполнения. <b> <b> platformBrowserDynamic()</b> </b> указывает Angular, что мы работаем в браузере
Узнайте больше о корневом модуле и начальной загрузке в Angular
2. Angular инициализирует компонент из списка <b> <b> bootstrap</b> </b> , указанного в <b> <b> app.module.ts</b> </b> (в нашем случае это <b> <b> HelloWorldComponent</b> </b> )
3. Angular ищет в документе элемент, соответствующий селектору, определенному в <b> <b> HelloWorldComponent</b> </b> (в нашем случае это <b> <b> 'hello-world'</b> </b> ) и вставляет компонент внутрь этого элемента
Готово! На следующей странице вы запустите свое первое <b> <b> Angular</b> </b> -приложение
Теперь, когда у нас есть и NgModule, и готовый компонент, давайте загрузим приложение!
Пока Angular загружается, содержимое элемента остается неизменным: в нашем случае, "<b> <b> Loading...</b> </b> "
Мы пишем браузерное веб-приложение, поэтому в списке <b> <b> imports</b> </b> необходимо указать <b> <b> BrowserModule</b> </b>
<div> <div>
Angular применяется не только для веб-приложений. Вы также можете создавать мобильные приложения и даже VR-зарисовки.
</div> </div>
<div> <div>
<a> <a></a> </a>
</div> </div>
Без системы внедрения зависимостей (Dependency Injection), свойство <b> <b> profession</b> </b> должно быть инициализировано внутри класса <b> <b> Person</b> </b>
При использовании системы внедрения зависимостей (Dependency Injection), класс <b> <b> Person</b> </b> просто запрашивает объект <b> <b> Job</b> </b> в конструкторе. Angular инстанциирует зависимость и передает результат классу.
При использовании системы внедрения зависимостей Angular сделает это за вас.
Также внедрение зависимостей (Dependency Injection) значительно упрощает тестирование, так как для этого необходимо просто подать "ложные" зависимости (Mock Dependecies) в параметры конструктора
Предположим, что у нас есть существующий <b> <b> UnitConverterService</b> </b> и мы хотели бы использовать его в <b> <b> UnitConversionComponent</b> </b> . Для этого необходимо проделать 3 простых шага:
Мы помечаем класс декоратором <b> <b> @Injectable()</b> </b> , что позволяет Angular понять, что этот класс будет использован в системе внедрения зависимостей.
Если сервисный класс отмечен декоратором @Injectable(), он может запрашивать другие сервисы в конструкторе
Передайте все экспортируемые (injectable) зависимости в секцию <b> providers</b> вашего <b> NgModule</b>
Теперь этот сервис становится доступным для всех <b> компонентов</b> и других сервисов в <b> NgModule</b> .
Благодаря <b> <b> private</b> </b> модификатору доступа, сервис становится доступным через класс как <b> <b> this.converter</b> </b>
На следующем слайде вы используете (и внедрите) videoService, в котором будет еще больше котиков!!! Результат будет выглядеть вот так:
[(ngModel)] - <b> <b> Banana in the box</b> </b> — мнемоника для такого порядка скобок
У Angular очень выразительная система шаблонов, основанная на HTML, которую можно расширить новыми элементами
В двойные фигурные скобки записывается нужное свойство компонента
Обратные кавычки <b> <b> ` `</b> </b> — волшебные кавычки, позволяющие делать переносы строк и использовать строковую интерполяцию
Также можно использовать простые выражения: вы можете вызвать метод компонента (как fullName() ниже) или вычислить <code> <code> 323213+34234</code> </code>
На следующем слайде отредактируйте шаблон компонента, чтобы создать простой заголовок и форму поиска. Результат должен выглядеть следующим образом:
Строковая интерполяция <b> <b>{{ curlies }} {{ curlies }}</b> </b> также позволяет передавать значения в атрибуты дочерних элементов
Но лучше использовать property binding (привязку свойств) <b> <b> [attribute]="property"</b> </b>
Angular поддерживает более продвинутые привязки свойств, чем просто имя атрибута
Это условное выражение добавляет или удаляет DOM элемент по условию
На следующем слайде добавьте обработчик нажатий кнопки поиска и отображение сообщения для случая, когда ни одно видео не было найдено. Результат должен выглядеть следующим образом:
Допустим, у нас есть список щенков, и мы хотим отобразить их на странице. Для этого в Angular есть специальный синтаксис — <b> <b> *ngFor</b> </b>
Давайте посмотрим, как это работает на следующем слайде
Для каждого щенка в массиве всех щенков<b> <b> *ngFor</b> </b> повторяет тот HTML-элемент, в котором и указан (в этом случае li)
HTML-атрибуты в <b> <b> Angular</b> </b> регистрозависимы:
<b> <b><s> <s> *ngfor</s> </s></b> </b> не сработает, а <b> <b> *ngFor</b> </b> сработает
На следующем слайде вы, наконец, покажете видео! Результат будет выглядеть следующим образом:
<b> <b> JavaScript</b> </b> – отличный язык, однако, при его использовании есть некоторая специфика:
<b> <b> ES</b> </b> расшифровывается как
<a> <a><b> <b> ECMAScript</b> </b></a> </a> - стандарт языка JavaScript.
Кроме этого, TypeScript расширяет систему типов и декораторов
Декораторы выглядят как @twitter_handles, мы изучим их позже
Ниже приведена функция <b> <b> add</b> </b> , где мы складываем 2 + 2. Что может пойти не так?
Оказывается, можно передать строку в качестве параметра и получить
<b> <b> 22</b> </b> вместо <b> <b> 4</b> </b> . Давайте посмотрим, как TypeScript поможет нам это предотвратить.
В TypeScript для указания типа используется "<b> <b> :</b> </b> " (например,
<b> <b> n: number</b> </b> ). Как <b> <b> a</b> </b> , так и <b> <b> b</b> </b> должны быть числами. Мы указали тип <b> <b> a</b> </b> , теперь ваша очередь!
Код выше можно редактировать
Здесь каждый элемент в массиве <b> <b> betterCats</b> </b> — экземпляр интерфейса <b> <b> Cat</b> </b> .
Они похожи на классы в других языках программирования и используются для группировки методов и свойств
В классе есть специальный метод: <b> <b> constructor</b> </b> , который вызывается при создании класса и позволяет записать полученные параметры в свойства класса.
Параметры конструктора, отмеченные как <b> <b> public </b> </b> (или private, или protected), записываются в свойства класса. Они будут доступны как <b> <b> this.ParameterName </b> </b> внутри класса.
Свойства 'private' или 'protected' не могут быть использованы вне класса.
Возможно, вы заметили оператор <b> <b> export </b> </b> перед классом.
Он используется для того, чтобы сделать класс доступным в других файлах. На следующем слайде мы покажем вам, как импортировать и использовать тот класс в другом файле.
"<b> <b> filter</b> </b> " - метод массива, который позволяет генерировать новый массив, выбрав только значения, удовлетворяющие условию
TypeScript поддерживает множество других интересных и полезных штук, например:
Стрелочные функции
Мы не будем рассматривать их подробно, вы можете ознакомиться с ними на сайте <a> <a> TypeScript </a> </a>
Ваша задача - создать TypeScript класс и назвать его CodeLab. При создании он получит список гостей. Также мы создадим метод, который выведет список тех, кто придет.
ES7
Декораторы
Типы
TypeScript
Классы
Модули
Далее...
Используйте клавиши <b> <b> ←</b> </b> и <b> <b> →</b> </b> на клавиатурe для навигации по слайдам.
Необходимые знания
Опишите ошибку, или поделитесь идеями
Отлично
Хорошо
Норм
Не очень
Оцените этот урок ...
Узнайте как быстро начать работу над вашим Angular приложением
Дерево компонентов
Научитесь выстраивать взаимосвязь между компонентами
Формы
Добавьте несложные формы в ваше приложение
Material Design
Узнайте как использовать библиотеку Angular Material
Маршрутизация
Узнайте как добавить маршрутизацию в ваше приложение
Этот курс написан на Angular и <a> <a> доступен на Github</a> </a> . Пожалуйста, поставьте ⭐, если вам понравилось! :)
Привет, я - <b> <b> angular-cli</b> </b> , я инструмент для командой строки!
Прежде всего убедитесь, что на вашем компьютере есть node.js.
Откройте командную строку и введите: <code> <code> node -v</code> </code>
Если увидите ошибку, следуйте <a> <a> инструкциям по настройке Node.js</a> </a>
Перейдите в папку с только что созданным приложением и запустите его командой <code> <code> ng serve</code> </code>
Когда приложение запустится, откройте
<a> <a> http://localhost:4200/</a> </a> в вашем браузере
Вы можете сгенерировать новые компоненты командой
<code> <code> ng generate component my-component</code> </code>
Вы также можете генерировать модули, сервисы и пайпы
Можно использовать упрощенную версию: <code> <code> ng g c my-component</code> </code>
У нас есть несложная форма, давайте разберемся как привязать ее значения к параметрам компонента
Сначала необходимо добавить <b> <b> FormsModule</b> </b> в наш NgModule
Далее мы можем использовать ngModel, чтобы привязать поля ввода к соответствующим параметрам компонента.
Пропробуйте поменять значение полей ввода, чтобы обновить значения
Давайте сделаем поле "username" обязательным (<b> <b> required</b> </b> )
Теперь отобразим валидационную ошибку. Для этого нужно проделать следующее:
Получить доступ к модели (ngModel) поля ввода с помощью <b> <b> #name="ngModel"</b> </b>
Использовать свойство <b> <b> errors </b> </b> у свойства <b> <b> usernameModel</b> </b>
Попробуйте очистить поле username, чтобы увидеть ошибку
Ниже представлен список <a> <a> встроеных валидаторов</a> </a> , которые используются в Angular
min - Минимальное значение (для чисел)
max - Максимальное значение (для чисел)
required - Обязательное значение
requiredTrue - Обязательное правдивое значение для флага
email - Соответствие регулярному выражению email'а
minLength - Минимальная длина текстового поля
maxLength - Максимальная длина текстового поля
pattern - Регулярное выражение для текстового поля
Можно также создавать собственные валидаторы, узнайте больше: <a> <a> здесь</a> </a>
Осталось решить небольшую проблему: если не указывать изначальные значение полей ввода, ошибка будет отображена сразу
Мы можем проверить, взаимодействовал ли пользователь с полем ввода с помощью свойства <b> <b> touched</b> </b> .
Значение <b> <b> touched</b> </b> положительно, если юзер сфокусировался на поле ввода и после убрал фокус, не меняя значение.
Попробуйте добавить/убрать фокус с поля ввода, или добавить/удалить значение, чтобы увидеть ошибку.
Теперь давайте сделаем нашу форму красивой с помощью Material Design.
Основные строительные блоки:
<b> <b> mat-form-field</b> </b> - Обертка вокруг поля ввода
<b> <b> matInput</b> </b> - Аттрибут должен быть добавлен на поле ввода
<b> <b> mat-error</b> </b> - Умная обертка для ошибок
Обратите внимание, что нам больше не нужно добавлять <b> <b> #name</b> </b> на поле ввода.
На следующем слайде будет упражнение, в котором мы добавим форму на страницу загрузки.
Обратите внимание: вам нужно будет вручную нажать на кнопку upload, чтобы увидеть результат
Есть два основных способа работы с формами в Angular. Пока раздел <b> <b> Продвинутых форм</b> </b> в разработке, прочитайте <a> <a> документацию Angular </a> </a>
Быстрые
Гибкие в использовании
Поддерживают Accessibility(доступность в использовании)
Оптимизированы для Angular
Отлично выглядят на мобильных устройствах
Вы можете посмотреть список компонентов
<a> <a> здесь</a> </a>
Добавьте <b> <b> MatToolbarModule</b> </b> в импорты
Используйте компонент в шаблоне
Обратите внимание, что анимации в Angular вынесены в отдельный модуль. Мы добавляем
<b> <b> NoopAnimationsModule</b> </b> здесь, но могли бы использовать
<b> <b> BrowserAnimationsModule</b> </b> вместо того, чтобы получить полноценные анимации.
Теперь давайте добавим material card
Заголовок и картинка
Добавим пару кнопок
Обратите внимание, что мы используем аттрибут <b> <b> mat-button</b> </b> вместо отдельного тега.
Indigo
Deep purple
Pink
Purple
На следующем слайде будет упражнение. Мы применим Angular Material к нашему приложению.
Примечание: В результате наше приложение не будет выглядеть так прекрасно как могло бы, но мы работаем над этим.
Котятки
Щеночки
Настройте машрутизацию, привязав компоненты к соответствующим URL
Создайте меню
Выберите место, где отобразить выбранный компонент
Потом передадим конфигурацию в наш модуль
Обратите внимание, что мы используем <b> <b> RouterModule.forRoot</b> </b> , который превращает нашу конфигурацию в Модуль Angular.
Теперь выберем место, где роутер отобразит выбранный компонент.
Мы можем сделать это положив тег <b> <b> router-outlet</b> </b> в нужное место в нужном компоненте
Осталось создать меню
Используйте директиву <b> <b> routerLink</b> </b> чтобы передать ссылку
На следующем слайде будет упражнение, в котором мы добавим два роута:
<b> <b> Search</b> </b> и <b> <b> Upload</b> </b> и меню для переключения между ними
Мы уже создали пустой компонент Upload и SearchComponent, содержащий логику для поиска.
Изучите <a> <a> Angular Router Guide</a> </a> чтобы подняться на более продвинутый уровень.
Без системы внедрения зависимостей, вам придется разбираться со всеми параметрами самостоятельно.
Добро пожаловать в интерактивный курс по Angular. Здесь вы сможете выучить основы <a> <a> Angular</a> </a> !
app.module.ts: Добавьте MatCardModule и MatToolbarModule в свойство "imports".
app.html: Добавьте material toolbar, содержащий значение свойства title
video/video.component.html: Используйте material card, чтобы отобразить дату
video/video.component.html: Добавьте mat-card-title (внутри of the mat-card), чтобы отобразить заголовок видео (title)
video/video.component.html: Добавьте mat-card-subtitle (внутри mat-card), чтобы отобразить описание (description)
video/video.component.html: Добавьте к картинке (img) аттрибут mat-card-image и ее ширина увеличится до ширины mat-card
video/video.component.html: Перенесите дату/информацию о просмотрах/лайках в mat-card-content (внутри mat-card)
app.module.ts: Используя RouterModule.forRoot, передайте пустой массив в модуль
app.module.ts: Добавьте роут с пустым ('') путем, отображающий SearchComponent
app.module.ts: Добавьте роут с путем ('upload'), отображающий UploadComponent
app.html: Добавьте router-outlet
Следующий слайд
Показать только следующий шаг
Введение
Установка
Создание нового приложение на Angular
Запускаем приложение
Генерируем компоненты
Упражнение 1
Узнайте, как настроить роутинг в вашем Angular приложении
Шаг 1
Декораторы
Упражнение
Шаг 2
К разделу "Шаблоны"
Пример зависимости
Сравнение
Шаг 3
Узнайте, как комбинировать компоненты
Простая форма
NgModel
Валидация
Отображение ошибки валидации
Валидаторы
Свойства Touched & Dirty
Material инпуты (бонус!)
Теперь сделаем наше приложение более привлекательным при помощи
<a> <a> Angular Material</a> </a> ✨✨
MatCard
Заголовок MatCard
MatButton
Темы оформления
Узнайте, как создавать простые формы в Angular
Конфигурация
Меню
Узнайте, как создавать красивые интерфейсы с помощью Angular Material
Узнайте, как использовать Angular Dependency Injection
Система типов
Классы
Узнайте, как создать ваше первое Angular приложение!
Angular CLI — инструмент для командной строки, который можно использовать для ускорения работы с вашим Angular приложением
Передача данных от родительского компонента к дочернему
Начнем с создания Angular <b> <b> Component </b> </b> 'а. Компоненты в Angular отвечают за визуальную часть приложения.
Как и компонент, модуль в <b> <b> Angular</b> </b> - это просто класс 👍(JavaScript class)
Как и компонент, модуль в <b> <b> Angular</b> </b> оборачивается в декоратор, в котором задается конфигурация модуля
При помощи готовых директив, включенных в Angular, можно легко добавить валидацию инпутов
Если вы очистите инпут, он будет помечен ошибкой, однако при этом никакой ошибки показано не будет. На следующем слайде мы узнаем, как их отображать.
Прочитайте больше о темах Angular Material
<a> <a> в этом руководстве</a> </a>
Роуты настраиваются с помощью создания списка, привязывающего URL-адресами к соответствующим компонентам
router-outlet
В этом примере, <b> <b> realPuppy</b> </b> — экземпляр интерфейса <b> <b> Puppy</b> </b> .
Angular-cli
Ошибка всегда показана
Angular написан на TypeScript, который, в свою очередь, является расширением JavaScript. Узнайте больше о TypeScript.
Angular написан на TypeScript. Узнайте больше про основы языка.
JavaScript не поддерживает строгую типизацию, что затрудняет разработку крупных приложений
TypeScript добавляет новую функциональность из следующей версии JavaScript
node
позволяет легко создавать работающее приложение прямо из коробки и генерировать новые компоненты! Он также настраивает конфигурацию сборки.
Далее:
Angular Material предоставляет набор Angular компонентов в стиле
<a> <a> Material Design</a> </a> , которые имеют ряд преимуществ:
MatToolBar
Смотрите также <a> <a> руководство для начала работы с Angular Material</a> </a>
Таким образом, наш компонент будет зависеть от VideoService
Далее...
Показать все шаги
Декораторы в <b> <b> Angular </b> </b> добавляют специальную информацию к классу.
Условное отображение (*ngIf)
Этот курс написан на Angular
Поставьте нам ⭐ если вы выучили что-то полезное
Не надо меня бояться, меня легко использовать
Если в результате увидите число(номер версии), то все в порядке, переходите на следующий слайд.
Декораторы в TypeScript были созданы по образу и подобию аналогичной функциональности в языке Python.
Мы показываем захардкоженый список видеозаписей в нашем компоненте, но в реальном приложении мы бы загрузили их с сервера
Код для получения данных был бы выделен в отдельный класс (сервис) с именем VideoService
С ростом нашего приложения, увеличивается и число зависимостей. В свою очередь, зависимости будут обрастать собственными зависимостями. Держать это все под контролем вручную становится все сложнее и сложнее
Чтобы упростить это, у <b> <b> Angular</b> </b> есть механизм, который называется <b> <b> Dependency injection</b> </b>
app.module.ts: Добавьте FormsModule и MatInputModule в свойство "imports".
upload/upload.component.html: Добавьте поле ввода "title" и привяжите его к свойству title компонента
upload/upload.component.html: Добавьте текстовое поле (textarea) "description" и привяжите к свойству description компонента
Значение <b> <b> dirty</b> </b> положительно, если пользователь изменил значение текстового поля.
Узнайте, как начать работу с Angular приложением в разделе "Angular-cli"
Все компоненты Angular Material позволяют применять темы. Попробуйте различные темы, нажимая на кнопки ниже:
Router is used to give <b> <b> URLs</b> </b> to different parts of your app.
Новая функциональность последних версий стандарта JavaScript часто может не поддерживаться в некоторых браузерах
Поэтому был создан <b> <b> TypeScript</b> </b> . Так как TypeScript может быть скомпилирован в JavaScript, он поддерживает все версии современных браузеров.
TypeScript расширяет последнюю версию JavaScript
Интерфейсы в Typescript позволяют указывать, какие свойства и методы должны быть у объекта
Типы массивов указываются с помощью <b> <b> Array{{ '<' }} {{ '<' }} тип {{ '>' }} {{ '>' }}</b> </b> или <b> <b> Type[]</b> </b>
<b> <b> import</b> </b> и <b> <b> export</b> </b> предназначены не только для классов. Они работают и с переменными, функциями и другими конструкциями!
Accessors (Getters / Setters)
Имя
Email (не обязателен и будет спрятан)
Отправить
Исходный код открыт на 100% и доступен на
<a> <a>
Github
</a> </a>
Примитивные типы (string, number, и прочие...)
Теперь вы знаете достаточно <b> <b> TypeScript</b> </b> чтобы начать изучение
<b> <b> Angular</b> </b> ! Узнайте больше о TypeScript на
<a> <a> TypeScript веб-сайт</a> </a>
Дополнительные возможности привязки событий
Мы познакомимся с более удобным способом работы с текстовыми полями в разделе "Формы"
Настроить маршрутизацию в Angular можно в 3 шага:
Конец раздела "Маршрутизация"
Вы можете добавить material toolbar в два шага:
Конец раздела "angular-cli"
Выполните <code> <code> npm install -g @angular/cli</code> </code> , чтобы
установить
<b> <b> Angular cli</b> </b> на ваш компьютер
Чтобы создать новое Angular приложение, выполните:
<code> <code> ng new awesome-app</code> </code> , затем <code> <code> cd awesome-app</code> </code>
Конец раздела "angular-cli"
Для обработки действий пользователя можно использовать привязку событий (event-binding). Для этого мы оборачиваем имя события в скобки и передаем выражение-обработчик:
В качестве альтернативы использования скобок для обозначения привязки событий (<b> <b> (event)</b> </b> ), может быть использован префикс "on-", например <b> <b> on-click</b> </b> это то же самое что и <b> <b> (click)</b> </b> .
Angular также предоставляет удобный способ обработки горячих клавиш.
Попробуйте обновить сообщение, нажав Control + Enter в текстовом поле.
Чтобы получить доступ к HTML элементу или Angular компоненту из шаблона, можно пометить этот элемент как <b> <b> #name</b> </b> , и он станет доступным как <b> <b> name</b> </b> во всём шаблоне:
================================================
FILE: apps/codelab/src/locale/messages.xmb
================================================
]>
src/app/components/index/index.component.html:2,4
Create your first Angular app
src/app/components/index/index.component.html:5 src/app/codelabs/angular/templates/templates.component.html:119 Templates
src/app/components/index/index.component.html:6,8 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:40
Dependency-Injection
src/app/components/index/index.component.html:9 Component-Tree
src/app/components/index/index.component.html:10 Custom-Events
src/app/components/index/index.component.html:11,14
Angular is written in TypeScript, a superset of JavaScript. Learn
TypeScript.
src/app/components/index/index.component.html:15,17
Learn how to create and bootstrap your first Angular application
src/app/components/index/index.component.html:18,20
Learn how to use Angular templates
src/app/components/index/index.component.html:21,23
Learn how to provide dependencies to your code instead of hard-coding them
src/app/components/index/index.component.html:27,29
Learn how to structure your app with reusable components
src/app/components/index/index.component.html:30,32
Learn to bind to events.
src/app/components/index/index.component.html:40 Angular Codelab
src/app/components/index/index.component.html:41,44
Welcome to the interactive Angular Codelab. Here you can learn the basics
of <a> <a> Angular</a> </a> !
src/app/components/index/index.component.html:52 Learn TypeScript
src/app/components/index/index.component.html:53 Skip if you're familiar with TypeScript
src/app/components/index/index.component.html:59 Learn Angular
src/app/components/index/index.component.html:68,70
Or click here to see full contents...
src/app/components/slides/title-slide/title-slide.component.html:6,8
Use <b> <b> ←</b> </b> and <b> <b> →</b> </b> on your keyboard to navigate the slides.
src/app/components/slides/title-slide/title-slide.component.html:11 Prerequisites:
src/app/components/slides/closing-slide/codelab-closing-slide.component.html:5,10
This codelab is written in Angular and
<a> <a> available on Github</a> </a> . Please ⭐ if you enjoyed it.
../../libs/slides/src/lib/arrows/slides-arrows.component.html:12 Next slide
../../libs/feedback/src/lib/feedback-widget/feedback-widget.component.html:41 Enter your name
../../libs/feedback/src/lib/feedback-widget/feedback-widget.component.html:52,53 Email is optional and, will not displayed
../../libs/feedback/src/lib/feedback-widget/feedback-widget.component.html:57 Describe your issue or share your ideas
../../libs/feedback/src/lib/feedback-widget/feedback-widget.component.html:70,72
Send
../../libs/feedback/src/lib/feedback-rating/feedback-rating.component.html:2 Perfect
../../libs/feedback/src/lib/feedback-rating/feedback-rating.component.html:3 good
../../libs/feedback/src/lib/feedback-rating/feedback-rating.component.html:4 ok
../../libs/feedback/src/lib/feedback-rating/feedback-rating.component.html:5 Hoped for more
../../libs/feedback/src/lib/feedback-rating/feedback-rating.component.html:12 Rate this lesson ...
src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.component.html:9 This Codelab is written in Angular!
src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.component.html:10,15
It's 100% open-source and is available on
<a> <a>
Github
</a> </a>
src/app/components/buttons-nav-bar/menu-github-widget/menu-github-widget.component.html:16 Please ⭐ the repo if you find it useful.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:2,4
Or use shorthand function notation.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:5,7
Error: this is clearly not a puppy
src/app/codelabs/angular/typescript/typescript/typescript.component.html:8 This is a number
src/app/codelabs/angular/typescript/typescript/typescript.component.html:9,11
Or use shorthand function notation.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:12,14
(Also called arrow function)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:15,17
Actually TypeScript can infer number here;
src/app/codelabs/angular/typescript/typescript/typescript.component.html:18,20
TypeScript can infer it's a string.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:21,23
Can't add number and boolean
src/app/codelabs/angular/typescript/typescript/typescript.component.html:24 Can't slice a number
src/app/codelabs/angular/typescript/typescript/typescript.component.html:25 But can slice a string!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:26 Works!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:27,29
Type[] does the same thing.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:30 This is a method.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:31,33
That's how russian dogs talk.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:34,36
Now we can instantiate (create) it
src/app/codelabs/angular/typescript/typescript/typescript.component.html:37 And use its methods
src/app/codelabs/angular/typescript/typescript/typescript.component.html:38,40
Later we'll have code here
src/app/codelabs/angular/typescript/typescript/typescript.component.html:41,43
Let's create more puppies
src/app/codelabs/angular/typescript/typescript/typescript.component.html:44,46
Var is still allowed but not recommended.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:47,49
Let should be used instead of var.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:53,55
Unlike var let is unavailable outside of this if.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:56,58
Const is like let, but if you try to change it, TS will give you an error.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:59,61
okay, definitely a boolean
src/app/codelabs/angular/typescript/typescript/typescript.component.html:62,64
Create a class called 'Codelab'
src/app/codelabs/angular/typescript/typescript/typescript.component.html:65 src/app/codelabs/angular/create-first-app/create-first-app.component.html:24 Export the class
src/app/codelabs/angular/typescript/typescript/typescript.component.html:66 Add a constructor
src/app/codelabs/angular/typescript/typescript/typescript.component.html:70,72
Make constructor take a parameter 'guests'
src/app/codelabs/angular/typescript/typescript/typescript.component.html:73,76
Specify the type for the guests parameter (hint: it's an array of a type
Guest)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:77,80
Make the parameter public (note that now you can access it anywhere in the
class using this.guests)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:84,86
Create new method 'getGuestsComing'
src/app/codelabs/angular/typescript/typescript/typescript.component.html:88,91
"b" in the code below is highlighted, because TypeScript is missing the
type. Specify the type for b.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:95,98
With this information TypeScript can highlight the error. Fix it, make 2 + 2
= 4 again!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:103,106
Modify getGuestsComing to filter the guests array return an array of guests
with the 'coming' property set to true.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:115 TypeScript
src/app/codelabs/angular/typescript/typescript/typescript.component.html:117 Angular is written in TypeScript. Learn more about the language basics.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:119 Basic understanding of JavaScript is required.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:125 Why TypeScript
src/app/codelabs/angular/typescript/typescript/typescript.component.html:126,128
<b> <b> JavaScript</b> </b> is a great language, but there's space for improvement:
src/app/codelabs/angular/typescript/typescript/typescript.component.html:130,133
JS is not type safe which makes it harder to develop large scale
applications
src/app/codelabs/angular/typescript/typescript/typescript.component.html:134,137
New features of the latest versions of JS standards (ES2018, ES2019) are
not supported well across all the browsers
src/app/codelabs/angular/typescript/typescript/typescript.component.html:139,143
<b> <b> ES</b> </b> stands for
<a> <a><b> <b> ECMAScript</b> </b></a> </a> , which is the name of the JavaScript language specification (standard)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:147 TypeScript
src/app/codelabs/angular/typescript/typescript/typescript.component.html:148,151
This is why <b> <b> TypeScript</b> </b> has been created. Since TypeScript can be
compiled to JavaScript, it can be used in any modern browser.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:155 TypeScript extends the latest version of JavaScript
src/app/codelabs/angular/typescript/typescript/typescript.component.html:156,158
TypeScript adds new features from the next version of JavaScript
src/app/codelabs/angular/typescript/typescript/typescript.component.html:159,161
On top of it, TypeScript adds an optional type system and decorators
src/app/codelabs/angular/typescript/typescript/typescript.component.html:168,170
Decorator looks like @twitter_handles, we'll learn more about them later
src/app/codelabs/angular/typescript/typescript/typescript.component.html:180 src/app/codelabs/angular/typescript/typescript/typescript.component.html:200 Type System
src/app/codelabs/angular/typescript/typescript/typescript.component.html:181,184
Below we have an <b> <b> add</b> </b> function, and we're adding 2 and 2. What could
go wrong?
src/app/codelabs/angular/typescript/typescript/typescript.component.html:191,195
Turns out it's possible to pass a string to this function and we get
<b> <b> 22</b> </b> instead of <b> <b> 4</b> </b> . Let's see how TypeScript can help address
this issue on the next slide
src/app/codelabs/angular/typescript/typescript/typescript.component.html:201,205
TypeScript uses "<b> <b> :</b> </b> " to specify the type information (e.g.
<b> <b> n: number</b> </b> ). Both <b> <b> a</b> </b> and <b> <b> b</b> </b> should be numbers. We
specified the type for <b> <b> a</b> </b> , now it's your turn!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:211 The code above is editable!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:216,217 Primitives (strings, numbers, etc...)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:218 Below are more types we can use
src/app/codelabs/angular/typescript/typescript/typescript.component.html:232 Interfaces
src/app/codelabs/angular/typescript/typescript/typescript.component.html:233,236
TypeScript Interfaces allow to specify properties and methods for an
object.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:246,248
Here, <b> <b> realPuppy</b> </b> is an implementation of the <b> <b> Puppy</b> </b> Interface.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:252 Arrays
src/app/codelabs/angular/typescript/typescript/typescript.component.html:253,256
Array types are defined as <b> <b> Array{{ '<' }} {{ '<' }} Type{{ '>' }} {{ '>' }}</b> </b> or
<b> <b> Type[]</b> </b>
src/app/codelabs/angular/typescript/typescript/typescript.component.html:263,266
Here, each element in the <b> <b> betterCats</b> </b> array is an instance of the
<b> <b> Cat</b> </b> Interface.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:271 Classes
src/app/codelabs/angular/typescript/typescript/typescript.component.html:272 TypeScript has <b> <b> classes</b> </b> , and Angular uses them heavily.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:273,276
They are similar to classes in other languages, and are used to group
methods and properties together
src/app/codelabs/angular/typescript/typescript/typescript.component.html:286 Constructor
src/app/codelabs/angular/typescript/typescript/typescript.component.html:287,290
There's a special method on the class called <b> <b> constructor</b> </b> . It's run
when the class is instantiated and allows the class to take parameters
src/app/codelabs/angular/typescript/typescript/typescript.component.html:300 Access Modifiers
src/app/codelabs/angular/typescript/typescript/typescript.component.html:301,305
Constructor parameters marked as <b> <b> public</b> </b> (or private, or protected),
become class properties accessible as <b> <b> this.ParameterName</b> </b> within the
class
src/app/codelabs/angular/typescript/typescript/typescript.component.html:312,314
private or protected properties are not visible outside of the class.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:318 Export
src/app/codelabs/angular/typescript/typescript/typescript.component.html:319,323
By the way, did you notice the <b> <b> export</b> </b> keyword before class? It is
used to share information between files. In the next slide, we'll show you
how to import and use this class in a different file
src/app/codelabs/angular/typescript/typescript/typescript.component.html:338 Import
src/app/codelabs/angular/typescript/typescript/typescript.component.html:339 Now we can use the <b> <b> Puppy</b> </b> class in the other file
src/app/codelabs/angular/typescript/typescript/typescript.component.html:347,350
<b> <b> import</b> </b> and <b> <b> export</b> </b> keywords are not just for classes. They
work with variables, functions and other things!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:354 Filter (One last thing)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:355,358
"<b> <b> filter</b> </b> " is an Array method that allows you to generate a new array
keeping only values that satisfy the condition
src/app/codelabs/angular/typescript/typescript/typescript.component.html:366 More
src/app/codelabs/angular/typescript/typescript/typescript.component.html:367 TypeScript supports lots of other cool features such as:
src/app/codelabs/angular/typescript/typescript/typescript.component.html:374 Enums
src/app/codelabs/angular/typescript/typescript/typescript.component.html:382 Async / Await
src/app/codelabs/angular/typescript/typescript/typescript.component.html:390 Accessors (Getters / Setters)
src/app/codelabs/angular/typescript/typescript/typescript.component.html:398 Destructuring
src/app/codelabs/angular/typescript/typescript/typescript.component.html:406 Arrow functions
src/app/codelabs/angular/typescript/typescript/typescript.component.html:409 And more!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:411,415
We won't cover them in detail, check out the
<a> <a> TypeScript</a> </a>
website!
src/app/codelabs/angular/typescript/typescript/typescript.component.html:419 src/app/codelabs/angular/create-first-app/create-first-app.component.html:196 src/app/codelabs/angular/create-first-app/create-first-app.component.html:304 src/app/codelabs/angular/create-first-app/create-first-app.component.html:417 src/app/codelabs/angular/templates/templates.component.html:170 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:278 src/app/codelabs/angular/router/router.component.html:125 src/app/codelabs/angular/material/material.component.html:181 src/app/codelabs/angular/forms/forms.component.html:183 Exercise
src/app/codelabs/angular/typescript/typescript/typescript.component.html:420 In the next slide we have a TypeScript exercise
src/app/codelabs/angular/typescript/typescript/typescript.component.html:421,425
Your task is to build a TypeScript class called Codelab which will take a
list of guests, and will have a method to output only the ones who are
coming.
src/app/codelabs/angular/typescript/typescript/typescript.component.html:426 The result will be as follows:
src/app/codelabs/angular/typescript/typescript/typescript.component.html:446 src/app/codelabs/angular/templates/templates.component.html:377 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:303 src/app/codelabs/angular/component-tree/component-tree.component.html:266 Milestone Completed
src/app/codelabs/angular/typescript/typescript/typescript.component.html:449,453
Now you should know enough <b> <b> TypeScript</b> </b> to start learning
<b> <b> Angular</b> </b> ! Read more about TypeScript on
<a> <a> TypeScript web site</a> </a>
src/app/codelabs/angular/typescript/typescript/typescript.component.html:459 src/app/codelabs/angular/create-first-app/create-first-app.component.html:510 src/app/codelabs/angular/templates/templates.component.html:382 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:308 src/app/codelabs/angular/router/router.component.html:168 src/app/codelabs/angular/material/material.component.html:227 src/app/codelabs/angular/forms/forms.component.html:224 Next:
src/app/codelabs/angular/typescript/typescript/typescript.component.html:460,462
Learn how to create your first Angular app!
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:66,68
ES7
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:78,80
Decorators
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:90,92
Types
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:102,104
TypeScript
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:114,116
Classes
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:126,128
Modules
src/app/codelabs/angular/typescript/typescript/typescript-svg/typescript-svg.component.html:138,140
More...
src/app/components/tests/simple-tests.component.html:27 see only next step
src/app/components/tests/simple-tests.component.html:28 see all steps
src/app/codelabs/angular/create-first-app/create-first-app.component.html:2,4
@Component is an Angular decorator
src/app/codelabs/angular/create-first-app/create-first-app.component.html:5,7
No semicolon here (as it attaches itself to the class below
src/app/codelabs/angular/create-first-app/create-first-app.component.html:8,10
The Decorator goes directly above the decorated entity (class in this case)
src/app/codelabs/angular/create-first-app/create-first-app.component.html:11,13
Component name is the class name (AppComponent).
src/app/codelabs/angular/create-first-app/create-first-app.component.html:14,16
Create a class called 'AppComponent'
src/app/codelabs/angular/create-first-app/create-first-app.component.html:17,19
Create a class called 'AppModule'
src/app/codelabs/angular/create-first-app/create-first-app.component.html:20,22
All set! Bootstrap your application
src/app/codelabs/angular/create-first-app/create-first-app.component.html:25,27
Add a Component decorator for the class
src/app/codelabs/angular/create-first-app/create-first-app.component.html:28,30
Add a selector to the component decorator and set it to 'my-app'
src/app/codelabs/angular/create-first-app/create-first-app.component.html:31,33
Add a template that contains: h1 with a text "Hello MewTube!"
src/app/codelabs/angular/create-first-app/create-first-app.component.html:34,36
Add a NgModule decorator for the class
src/app/codelabs/angular/create-first-app/create-first-app.component.html:37,39
Add 'BrowserModule' to the NgModule decorator imports
src/app/codelabs/angular/create-first-app/create-first-app.component.html:43,45
Add 'AppComponent' to the 'declarations' property of the decorator
src/app/codelabs/angular/create-first-app/create-first-app.component.html:46,48
Add 'AppComponent' to the 'bootstrap' property of the decorator
src/app/codelabs/angular/create-first-app/create-first-app.component.html:56 Create your first Angular app
src/app/codelabs/angular/create-first-app/create-first-app.component.html:58 You will learn how to create your first Angular component, put it in a module, and bootstrap the app.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:60 Knowing TypeScript basics would help a lot
src/app/codelabs/angular/create-first-app/create-first-app.component.html:67 What is Angular?
src/app/codelabs/angular/create-first-app/create-first-app.component.html:69,75
Angular is a <b> <b> development platform</b> </b> for building mobile and desktop
applications. Angular lets you <b> <b> extend HTML's syntax</b> </b> to express
your application's components clearly and succinctly. Angular's binding
and Dependency Injection <b> <b> eliminate much of the code</b> </b> you would
otherwise have to write.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:81 src/app/codelabs/angular/create-first-app/create-first-app.component.html:97 src/app/codelabs/angular/templates/templates.component.html:129 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:107 src/app/codelabs/angular/router/router.component.html:39 src/app/codelabs/angular/router/router.component.html:61 src/app/codelabs/angular/angular-cli/angular-cli.component.html:16 Intro
src/app/codelabs/angular/create-first-app/create-first-app.component.html:82 Given an HTML file:
src/app/codelabs/angular/create-first-app/create-first-app.component.html:88,91
Let's create an Angular app which replaces the <b> <b> hello-world</b> </b> HTML
element with the app's contents.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:92 This can be done with 3 simple steps.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:98 The 3 steps are:
src/app/codelabs/angular/create-first-app/create-first-app.component.html:100 Create an Angular component
src/app/codelabs/angular/create-first-app/create-first-app.component.html:101 Create an Angular module
src/app/codelabs/angular/create-first-app/create-first-app.component.html:102 Bootstrap the module
src/app/codelabs/angular/create-first-app/create-first-app.component.html:108 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:224 Step 1
src/app/codelabs/angular/create-first-app/create-first-app.component.html:109,112
Start by creating an Angular <b> <b> Component</b> </b> . Components in Angular are
responsible for the visual part of the app
src/app/codelabs/angular/create-first-app/create-first-app.component.html:118,121
An Angular component is just a class. Properties and behavior can be added
inside.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:126 src/app/codelabs/angular/create-first-app/create-first-app.component.html:142 Decorators
src/app/codelabs/angular/create-first-app/create-first-app.component.html:134 The class is adorned with a <b> <b> @Component</b> </b> decorator
src/app/codelabs/angular/create-first-app/create-first-app.component.html:135,137
Decorators attach <b> <b> Angular</b> </b> specific information to the class.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:143,146
Decorators are a new feature of TypeScript. They attach metadata to a
class, function, property or variable
src/app/codelabs/angular/create-first-app/create-first-app.component.html:153,156
TypeScript decorators are inspired by a similar feature in the Python
language.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:161 Selector
src/app/codelabs/angular/create-first-app/create-first-app.component.html:162,166
Selectors define the location of the component. When Angular renders this
component, it'll find a <b> <b> hello-world</b> </b> HTML element in the document
and render the component inside of it
src/app/codelabs/angular/create-first-app/create-first-app.component.html:179 Inline Template
src/app/codelabs/angular/create-first-app/create-first-app.component.html:180 Template defines the HTML code that the component generates
src/app/codelabs/angular/create-first-app/create-first-app.component.html:188,191
If the amount of HTML grows out of hand, it's possible (and recommended)
to use a <b> <b> templateUrl</b> </b> instead and provide a path to the HTML file.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:197,200
In the next slide you'll create your first <b> <b> Angular</b> </b> component! We'll
do all the wiring for you. The result will look like this:
src/app/codelabs/angular/create-first-app/create-first-app.component.html:216 Create first Angular component!
src/app/codelabs/angular/create-first-app/create-first-app.component.html:222 src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:242 Step 2
src/app/codelabs/angular/create-first-app/create-first-app.component.html:223 Next step is to declare the component in an <b> <b> NgModule</b> </b> .
src/app/codelabs/angular/create-first-app/create-first-app.component.html:224,227
<b> <b> NgModule</b> </b> does not have any visual representation and is used
exclusively for grouping Angular building blocks together
src/app/codelabs/angular/create-first-app/create-first-app.component.html:228,230
We will learn more about NgModules in the future milestones
src/app/codelabs/angular/create-first-app/create-first-app.component.html:235 Module Class
src/app/codelabs/angular/create-first-app/create-first-app.component.html:236 Like a component, <b> <b> Angular</b> </b> module is just a class
src/app/codelabs/angular/create-first-app/create-first-app.component.html:249 NgModule Decorator
src/app/codelabs/angular/create-first-app/create-first-app.component.html:250,253
Like a component, <b> <b> Angular</b> </b> module is adorned with a decorator
providing metadata
src/app/codelabs/angular/create-first-app/create-first-app.component.html:266 Browser Module
src/app/codelabs/angular/create-first-app/create-first-app.component.html:272 Declarations
src/app/codelabs/angular/create-first-app/create-first-app.component.html:273,276
The <b> <b> Declarations array</b> </b> specifies components belonging to the
AppModule
src/app/codelabs/angular/create-first-app/create-first-app.component.html:288 Bootstrap
src/app/codelabs/angular/create-first-app/create-first-app.component.html:289,292
The component passed into the <b> <b> bootstrap</b> </b> array will be created and
displayed in your <b> <b> index.html</b> </b> file
src/app/codelabs/angular/create-first-app/create-first-app.component.html:305,309
In the next slide you'll create your first <b> <b> Angular</b> </b> module! We'll
use the component from the previous exercises and do all the wiring for
you. The result will look like this:
src/app/codelabs/angular/create-first-app/create-first-app.component.html:324 Create first NgModule.
src/app/codelabs/angular/create-first-app/create-first-app.component.html:330 Bootstrapping
src/app/codelabs/angular/create-first-app/create-first-app.component.html:331,333
We have everything ready, so now it's time to start (bootstrap) the app!
src/app/codelabs/angular/create-first-app/create-first-app.component.html:334,337
Passing your <b> <b> AppModule</b> </b> to the <b> <b> bootstrapModule</b> </b> method will
start up all the components from that module's bootstrap section
src/app/codelabs/angular/create-first-app/create-first-app.component.html:344,346
For most simple apps, you can just copy/paste the code above "as is"
src/app/codelabs/angular/create-first-app/create-first-app.component.html:350 Bootstrapping 1
src/app/codelabs/angular/create-first-app/create-first-app.component.html:351 How does bootstrapping work in Angular?
src/app/codelabs/angular/create-first-app/create-first-app.component.html:353,356
1. Kicks off execution environment. <b> <b> platformBrowserDynamic()</b> </b> tells
Angular that we are operating in the browser
src/app/codelabs/angular/create-first-app/create-first-app.component.html:367,368 src/app/codelabs/angular/create-first-app/create-first-app.component.html:388,389 src/app/codelabs/angular/create-first-app/create-first-app.component.html:411,412 Read more about root module and bootstrapping in Angular
src/app/codelabs/angular/create-first-app/create-first-app.component.html:373 Bootstrapping 2
src/app/codelabs/angular/create-first-app/create-first-app.component.html:374,377
2. Angular initializes the component from the <b> <b> bootstrap</b> </b> array in
<b> <b> app.module.ts</b> </b> (<b> <b> HelloWorldComponent</b> </b> in this case)
src/app/codelabs/angular/create-first-app/create-first-app.component.html:394 Bootstrapping 3
src/app/codelabs/angular/create-first-app/create-first-app.component.html:395,399
3. Angular looks in the document for an element matching the selector
defined in <b> <b> HelloWorldComponent</b> </b> (<b> <b> 'hello-world'</b> </b> in our case)
and inserts the component inside that element
src/app/codelabs/angular/create-first-app/create-first-app.component.html:418,420
All set! In the next page you'll bootstrap your first <b> <b> Angular</b> </b> app!
src/app/codelabs/angular/create-first-app/create-first-app.component.html:436,439
Now that we've got both NgModule and the component ready, let's
bootstrap the app!
src/app/codelabs/angular/create-first-app/create-first-app.component.html:447 src/app/codelabs/angular/component-tree/component-tree.component.html:201 Review
src/app/codelabs/angular/create-first-app/create-first-app.component.html:448 Loading order: index -> main -> app.module -> app.component
src/app/codelabs/angular/create-first-app/create-first-app.component.html:494,497
While Angular is loading, the contents of the element will stay the same
(<b> <b> Loading...</b> </b> ) in this case
src/app/codelabs/angular/create-first-app/create-first-app.component.html:503 End of Bootstrap Section
src/app/codelabs/angular/create-first-app/create-first-app.component.html:506 src/app/codelabs/angular/router/router.component.html:159 src/app/codelabs/angular/material/material.component.html:214 src/app/codelabs/angular/forms/forms.component.html:217 Well done! This is the end of the milestone!
src/app/codelabs/angular/create-first-app/create-first-app.component.html:511,513
Go to the templates Milestone
src/app/codelabs/angular/create-first-app/mode/mode.component.html:2,5
Because we're building a browser web app, we need to pass
<b> <b> BrowserModule</b> </b> to the <b> <b> imports</b> </b> array
src/app/codelabs/angular/create-first-app/mode/mode.component.html:26 With Angular we build mobile apps using NativeScript or Ionic.
src/app/codelabs/angular/create-first-app/mode/mode.component.html:42 With Angular you can build VR apps with A-FRAME or WEBGL.
src/app/codelabs/angular/create-first-app/mode/mode.component.html:57,70
<div> <div>
Angular is not just for web apps anymore; you can also use it to create
native phone apps and even VR scenes.
</div> </div>
<div> <div>
<a> <a></a> </a>
</div> </div>
src/app/codelabs/angular/templates/templates.component.html:2,4
This is valid HTML syntax.
src/app/codelabs/angular/templates/templates.component.html:5,7
It works on attribute syntax.
src/app/codelabs/angular/templates/templates.component.html:11,13
It allows to conditionally bind a class
src/app/codelabs/angular/templates/templates.component.html:14 Or style properties
src/app/codelabs/angular/templates/templates.component.html:15,17
And works with custom components!
src/app/codelabs/angular/templates/templates.component.html:21,24
When user clicks the button, it calls the "saveUser" function on the
component instance and passes the underlying event.
src/app/codelabs/angular/templates/templates.component.html:28,32
You can also create events for custom components. Here we have a depleted
event, and it's going to call the "soundAlarm" function on the component
instance when it fires.
src/app/codelabs/angular/templates/templates.component.html:36,40
There are also shortcut event bindings! The submit function on the component
instance will be called when the user presses control and enter (this is an
Angular feature).
src/app/codelabs/angular/templates/templates.component.html:41,43
userName has a reference to the input element
src/app/codelabs/angular/templates/templates.component.html:44,46
Try changing to true!
src/app/codelabs/angular/templates/templates.component.html:47,49
Need to repeat puppies here
src/app/codelabs/angular/templates/templates.component.html:50,52
app.component.ts: Add a 'videos' property, set the value as empty array.
src/app/codelabs/angular/templates/templates.component.html:53,56
app.component.ts: Inside of the 'search' method assign FAKE_VIDEOS, to the
component 'videos' property.
src/app/codelabs/angular/templates/templates.component.html:62,65
app.component.ts: Add a 'search' method on the component, that takes a
'searchString' parameter.
src/app/codelabs/angular/templates/templates.component.html:69,73
app.html: Add a click handler to the button, call 'search' method and pass
the input value (Actual search functionality will be implemented in the next
exercise)
src/app/codelabs/angular/templates/templates.component.html:74,77
app.html: Add a message saying 'no videos' which is displayed only when the
videos array is empty
src/app/codelabs/angular/templates/templates.component.html:82,85
#Bonus app.component.ts: Right now it takes pressing a search button to
display the videos. Instead display all videos by default.
src/app/codelabs/angular/templates/templates.component.html:89,93
app.component.ts: Inside of the 'search' method filter FAKE_VIDEOS and only
return videos with the title containing searchString. (hint: use .includes
or .indexOf string methods)
src/app/codelabs/angular/templates/templates.component.html:94,96
app.html: Also display a thumbnail
src/app/codelabs/angular/templates/templates.component.html:100,103
app.html: Iterate over the videos using '*ngFor', and display a title for
each
src/app/codelabs/angular/templates/templates.component.html:105,107
app.html: Add a button tag with a text 'search'
src/app/codelabs/angular/templates/templates.component.html:108,110
app.html: Add an input tag with a 'placeholder' attribute set to 'video'
src/app/codelabs/angular/templates/templates.component.html:121 Learn more about Angular templates!
src/app/codelabs/angular/templates/templates.component.html:130,133
Angular has a very expressive template system, which takes HTML as a base,
and extends it with custom elements
src/app/codelabs/angular/templates/templates.component.html:142 src/app/codelabs/angular/templates/templates.component.html:158 Interpolation
src/app/codelabs/angular/templates/templates.component.html:143,145
Double curlies include the appropriate component property value
src/app/codelabs/angular/templates/templates.component.html:150,153
Backticks <b> <b> ` `</b> </b> , are magic quotes that allow multi-line strings and
text interpolation.
src/app/codelabs/angular/templates/templates.component.html:159,162
Simple expressions are also allowed, you can run a component method (like
fullName() below), or calculate <code> <code> 323213+34234</code> </code>
src/app/codelabs/angular/templates/templates.component.html:171,174
In the next slide you'll edit a component template to create a simple
header and search form. The result will look like this:
src/app/codelabs/angular/templates/templates.component.html:193 Properties
src/app/codelabs/angular/templates/templates.component.html:194,197
String interpolation <b> <b>{{ curlies }} {{ curlies }}</b> </b> can also be used to pass a value
to a child element's attribute
src/app/codelabs/angular/templates/templates.component.html:206 Property Binding
src/app/codelabs/angular/templates/templates.component.html:207,209
Better option is to use property binding <b> <b> [attribute] = property</b> </b>
src/app/codelabs/angular/templates/templates.component.html:214,216
You can use arbitrary expressions in the binding.
src/app/codelabs/angular/templates/templates.component.html:221 Data binding extras
src/app/codelabs/angular/templates/templates.component.html:222,224
Angular supports more advanced property bindings than just attribute name
src/app/codelabs/angular/templates/templates.component.html:233 Event binding: (event)
src/app/codelabs/angular/templates/templates.component.html:234,238
For handling user actions we can use event bindings. To do that we wrap
the event name in parentheses, and pass an expression performing required
action:
src/app/codelabs/angular/templates/templates.component.html:245,248
While parentheses are used for event binding: <b> <b> (event)</b> </b> , "on-" can
also be used, e.g. <b> <b> on-click</b> </b> is the same as <b> <b> (click)</b> </b> .
src/app/codelabs/angular/templates/templates.component.html:252 src/app/codelabs/angular/templates/templates.component.html:270 Event binding shortcuts
src/app/codelabs/angular/templates/templates.component.html:253,257
When we need to access an HTML element or an Angular component
from the template, we can mark it with <b> <b> #name</b> </b> , and it becomes
available as <b> <b> name</b> </b> everywhere in the template:
src/app/codelabs/angular/templates/templates.component.html:264,266
We'll learn a better way to work with inputs in Forms milestone.
src/app/codelabs/angular/templates/templates.component.html:271,274
Angular also provides a shortcut for handling keyboard shortcuts. Try
updating the message by pressing Control + Enter in the input.
src/app/codelabs/angular/templates/templates.component.html:285 Conditional Display (*ngIf)
src/app/codelabs/angular/templates/templates.component.html:286,289
This conditional expression will add or remove an element from the DOM if
it evaluates as a truthy
src/app/codelabs/angular/templates/templates.component.html:297 src/app/codelabs/angular/component-tree/component-tree.component.html:244 Exercise 2
src/app/codelabs/angular/templates/templates.component.html:298,302
In the next slide you'll add a click handler to the search button, and
display a message for the case where no videos were found. The result will
look like this:
src/app/codelabs/angular/templates/templates.component.html:320 Repeating elements
src/app/codelabs/angular/templates/templates.component.html:321,325
Let's say you have an array of puppies, and want to display all of them on
the page. Angular has a special syntax for that called <b> <b> *ngFor</b> </b> ,
let's see how it works on the next slide
src/app/codelabs/angular/templates/templates.component.html:333 Repeating elements (*ngFor)
src/app/codelabs/angular/templates/templates.component.html:334,337
Here <b> <b> *ngFor</b> </b> repeats HTML element it's attached to (li in this case)
for every single puppy in the puppies array
src/app/codelabs/angular/templates/templates.component.html:343,346
HTML attributes in <b> <b> Angular</b> </b> are case sensitive:
<b> <b><s> <s> *ngfor</s> </s></b> </b> won't work, <b> <b> *ngFor</b> </b> will
src/app/codelabs/angular/templates/templates.component.html:350 Exercise 3
src/app/codelabs/angular/templates/templates.component.html:353,356
In the next slide you'll finally display the videos! The result will
look like this:
src/app/codelabs/angular/templates/templates.component.html:383,385
Learn how to use Angular Dependency Injection
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:5,7
* TypeScript shorthand makes 'profession' * available to component instance.
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:8,10
assuming Job has property '.title'
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:11,13
video.service.ts: Add @Injectable() decorator to the class
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:15,17
app.module.ts: Add VideoService to the NgModule providers property
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:18,20
app.component.ts: Get rid of FAKE_VIDEOS
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:21,24
app.component.ts: Inject 'VideoService' in the component constructor as
'videoService'
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:28,31
app.component.ts: Update the app component's search method to use
videoService's search method
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:42 Learn more about Angular's powerful Dependency Injection system
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:50 Example of a dependency
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:51,54
We display a list of hard-coded videos in our component, but in the real
world we'd load them from the server.
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:55,58
The code for fetching the data would live in a separate class (service)
called VideoService
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:59 Therefore our component will depend on the VideoService:
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:109,113
As our app grows, number of dependencies will increase. Dependencies, in
order, will have their own dependencies. Managing all of them manually
becomes increasingly harder.
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:114,117
To simplify that <b> <b> Angular</b> </b> provides a mechanism called
<b> <b> Dependency injection</b> </b>
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:124 Comparison
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:133,136
Without Dependency Injection, <b> <b> Profession</b> </b> has to be instantiated
in the <b> <b> Person</b> </b> class
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:145,149
With Dependency Injection, <b> <b> Person</b> </b> class just "requires" an
instance of <b> <b> Job</b> </b> in the constructor, and Angular takes care of
instantiating it
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:155 Parameters
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:164,167
Without Dependency Injection, you have to figure out all the
parameters yourself
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:176,178
With Dependency Injection, Angular takes care of it
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:184 Testing
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:185,188
Also Dependency Injection simplifies testing a lot, because you can just
pass mock dependencies as constructor parameters
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:209 Example
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:210,214
Let's say we have an existing <b> <b> UnitConverterService</b> </b> and we want to
start using it in <b> <b> UnitConversionComponent</b> </b> . It will take 3 simple
steps:
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:216 Mark dependency as @Injectable()
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:217 Provide in the module
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:218 Require in the component
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:225,228
Mark the class as <b> <b> @Injectable()</b> </b> . This lets Angular know that this
class is part of Angular Dependency Injection system
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:234,237
If a service class is marked as injectable, it can require other services
in its constructor.
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:243,245
Provide the injectable to the <b> <b> providers</b> </b> section of <b> <b> NgModule</b> </b>
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:254,257
Now, this service becomes available for every <b> <b> Component</b> </b> and other
service in this <b> <b> NgModule</b> </b> .
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:262 Step 3
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:263 Consume the Injectable in the component
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:271,274
Because of the <b> <b> private</b> </b> access modifier the service becomes
accessible across the class as <b> <b> this.converter</b> </b> .
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:279,282
In the next slide you'll use videoService which has even more cats!!! The
result will look like this:
src/app/codelabs/angular/dependency-injection/dependency-injection.component.html:309,311
Learn how to combine components together
src/app/codelabs/angular/component-tree/component-tree.component.html:5,8
video/video.component.ts: Add the '@Component' decorator and set its selector
property to 'my-video'.
src/app/codelabs/angular/component-tree/component-tree.component.html:9,11
app.module.ts: Add VideoComponent to the AppModule 'declarations'.
src/app/codelabs/angular/component-tree/component-tree.component.html:16,18
video/video.component.ts: Set the templateUrl to load the appropriate html file
src/app/codelabs/angular/component-tree/component-tree.component.html:22,24
video/video.component.ts: Add a video property and decorate it with @Input()
src/app/codelabs/angular/component-tree/component-tree.component.html:25,27
video/video.component.html: Display the video title
src/app/codelabs/angular/component-tree/component-tree.component.html:28,30
video/video.component.html: Display the video thumbnail (src)
src/app/codelabs/angular/component-tree/component-tree.component.html:31,33
video/video.component.html: Display the video description
src/app/codelabs/angular/component-tree/component-tree.component.html:34,36
video/video.component.html: Display the video date
src/app/codelabs/angular/component-tree/component-tree.component.html:37,39
video/video.component.html: Display the number of video views
src/app/codelabs/angular/component-tree/component-tree.component.html:40,42
video/video.component.html: Display the number video likes
src/app/codelabs/angular/component-tree/component-tree.component.html:43,46
app.html: Replace existing title and thumbnail with our shiny new my-video
component
src/app/codelabs/angular/component-tree/component-tree.component.html:50,53
app.html: Use the data binding to pass the video object to the component
(don't forget the square brackets)
src/app/codelabs/angular/component-tree/component-tree.component.html:67 src/app/codelabs/angular/component-tree/component-tree.component.html:76 Component Tree
src/app/codelabs/angular/component-tree/component-tree.component.html:69 Combine components together
src/app/codelabs/angular/component-tree/component-tree.component.html:77,80
So far we have only one component, but as your app grows it will form a
tree of components
src/app/codelabs/angular/component-tree/component-tree.component.html:86 Parent and Child
src/app/codelabs/angular/component-tree/component-tree.component.html:87,90
Any component can render another one by using an HTML element that matches
the selector of the other component
src/app/codelabs/angular/component-tree/component-tree.component.html:121 src/app/codelabs/angular/component-tree/component-tree.component.html:138 Passing Data from Parent to Child
src/app/codelabs/angular/component-tree/component-tree.component.html:123,125
Parent component passes its data to the child component via properties
src/app/codelabs/angular/component-tree/component-tree.component.html:126,129
Change the <b> <b> size</b> </b> to <b> <b> 100</b> </b> and <b> <b> color</b> </b> to <b> <b> red</b> </b> to
recreate the Japanese flag.
src/app/codelabs/angular/component-tree/component-tree.component.html:140,143
The child class must decorate its properties with a special
<b> <b> @Input()</b> </b> decorator
src/app/codelabs/angular/component-tree/component-tree.component.html:148,151
This is the first time we're applying decorators to properties (as opposed
to classes).
src/app/codelabs/angular/component-tree/component-tree.component.html:155 Exercise 1
src/app/codelabs/angular/component-tree/component-tree.component.html:156,159
We already know how to create a component. Let's move all the
video-related information into a new component called VideoComponent.
src/app/codelabs/angular/component-tree/component-tree.component.html:160,162
We will bootstrap the component for you; the result will be as follows:
src/app/codelabs/angular/component-tree/component-tree.component.html:165 Cute kitten
src/app/codelabs/angular/component-tree/component-tree.component.html:188 Parent and Child component
src/app/codelabs/angular/component-tree/component-tree.component.html:189,192
Components won't know about each other unless they're declared in the same
module
src/app/codelabs/angular/component-tree/component-tree.component.html:245 In the next exercise you will use the newly created component
src/app/codelabs/angular/component-tree/component-tree.component.html:271 Next:
src/app/codelabs/angular/component-tree/component-tree.component.html:272,274
Learn how to set up routing in your Angular app
src/app/codelabs/angular/router/router.component.html:2,5
app.module.ts: Using RouterModule.forRoot provide an empty array to the
module
src/app/codelabs/angular/router/router.component.html:6,9
app.module.ts: Add a route with an empty (\'\') path to display
SearchComponent
src/app/codelabs/angular/router/router.component.html:10,12
app.module.ts: Add a route with path of "upload" to display UploadComponent
src/app/codelabs/angular/router/router.component.html:13,15
app.html: Add router-outlet
src/app/codelabs/angular/router/router.component.html:32 Routing
src/app/codelabs/angular/router/router.component.html:33 Learn how to add routing to your app
src/app/codelabs/angular/router/router.component.html:40,42
Router is used to give <b> <b> URLs</b> </b> to different parts of your app.
src/app/codelabs/angular/router/router.component.html:49 Kittens
src/app/codelabs/angular/router/router.component.html:56 Puppies
src/app/codelabs/angular/router/router.component.html:62 It takes 3 steps to set up routing in Angular:
src/app/codelabs/angular/router/router.component.html:64,67
Configure the mapping and specify which component to display for which
URL
src/app/codelabs/angular/router/router.component.html:68 Create the menu
src/app/codelabs/angular/router/router.component.html:69 Find a place to display selected component
src/app/codelabs/angular/router/router.component.html:74 src/app/codelabs/angular/router/router.component.html:86 Configuration
src/app/codelabs/angular/router/router.component.html:75,78
Routes are configured by defining an array of mapping between URL path and
a component
src/app/codelabs/angular/router/router.component.html:87 Then we have to pass the config to our module.
src/app/codelabs/angular/router/router.component.html:92,95
Note that we're using <b> <b> RouterModule.forRoot</b> </b> which creates an Angular
Module out of our configuration.
src/app/codelabs/angular/router/router.component.html:99 router-outlet
src/app/codelabs/angular/router/router.component.html:100,103
Now we have to find a place where the router would display selected
component.
src/app/codelabs/angular/router/router.component.html:104 It can be marked by placing <b> <b> router-outlet tag</b> </b>
src/app/codelabs/angular/router/router.component.html:114 Menu
src/app/codelabs/angular/router/router.component.html:115 The last part is creating the menu.
src/app/codelabs/angular/router/router.component.html:116 Use <b> <b> routerLink</b> </b> directive to provide the URL
src/app/codelabs/angular/router/router.component.html:126,129
In the next slide there is an exercise. We're going to add 2 routes:
<b> <b> Search</b> </b> and <b> <b> Upload</b> </b> , and a menu for switching between them.
src/app/codelabs/angular/router/router.component.html:135,138
Note: We have already created empty Upload component and SearchComponent
containing search logic.
src/app/codelabs/angular/router/router.component.html:155,156 End of The Routing Milestone
src/app/codelabs/angular/router/router.component.html:160,164
Check out
<a> <a> Angular Router Guide</a> </a> for
more fun and advanced techniques.
src/app/codelabs/angular/router/router.component.html:169,171
Learn how to create beautiful UIs with Angular Material
src/app/codelabs/angular/material/material.component.html:3,5
app.module.ts: Add MatCardModule and MatToolbarModule, to the imports.
src/app/codelabs/angular/material/material.component.html:6,8
app.html: Add material toolbar containing the title
src/app/codelabs/angular/material/material.component.html:9,11
video/video.component.html: Use material card to display the data
src/app/codelabs/angular/material/material.component.html:12,15
video/video.component.html: Add mat-card-title (inside of the mat-card) to display
video title
src/app/codelabs/angular/material/material.component.html:16,19
video/video.component.html: Add mat-card-subtitle (inside of the mat-card) to
display video description
src/app/codelabs/angular/material/material.component.html:20,23
video/video.component.html: Mark img with mat-card-image attribute (inside of the
mat-card) so that it takes full card size
src/app/codelabs/angular/material/material.component.html:24,27
video/video.component.html: move date/views/likes info inside of mat-card-content
(inside of the mat-card)
src/app/codelabs/angular/material/material.component.html:39 Material design
src/app/codelabs/angular/material/material.component.html:40 Learn how to use Angular Material
src/app/codelabs/angular/material/material.component.html:45,49
Now let's use
<a> <a> Angular Material</a> </a>
to make our app look beautiful ✨✨
src/app/codelabs/angular/material/material.component.html:50,54
Angular Material provides you with a set of
<a> <a> Material Design</a> </a>
components for Angular:
src/app/codelabs/angular/material/material.component.html:58 Fast and Consistent
src/app/codelabs/angular/material/material.component.html:59 Versatile
src/app/codelabs/angular/material/material.component.html:60 Accessible
src/app/codelabs/angular/material/material.component.html:61 Optimized for Angular
src/app/codelabs/angular/material/material.component.html:62 Look great on mobile
src/app/codelabs/angular/material/material.component.html:65,72
See the list of components
<a> <a> here</a> </a>
src/app/codelabs/angular/material/material.component.html:76 MatToolBar
src/app/codelabs/angular/material/material.component.html:77 Adding a toolbar takes 2 steps:
src/app/codelabs/angular/material/material.component.html:79 Add <b> <b> MatToolbarModule</b> </b> to the imports
src/app/codelabs/angular/material/material.component.html:80 Use the component in the template
src/app/codelabs/angular/material/material.component.html:87,91
Note that Angular animations come in a separate module. We're including
<b> <b> NoopAnimationsModule</b> </b> here, but could have used
<b> <b> BrowserAnimationsModule</b> </b> instead, to have full-fledged animations.
src/app/codelabs/angular/material/material.component.html:95 Card
src/app/codelabs/angular/material/material.component.html:96 Now let's add material card.
src/app/codelabs/angular/material/material.component.html:104 Card Header
src/app/codelabs/angular/material/material.component.html:105 Header and an image.
src/app/codelabs/angular/material/material.component.html:113 Buttons
src/app/codelabs/angular/material/material.component.html:114 Adding some actions....
src/app/codelabs/angular/material/material.component.html:119,122
Note that for the button we're using <b> <b> mat-button</b> </b> attribute instead
of a separate tag.
src/app/codelabs/angular/material/material.component.html:126 Themes
src/app/codelabs/angular/material/material.component.html:127,130
All Angular Material components allow to apply themes. Try different
themes by clicking the buttons below:
src/app/codelabs/angular/material/material.component.html:138,140
Indigo
src/app/codelabs/angular/material/material.component.html:147,149
Deep purple
src/app/codelabs/angular/material/material.component.html:156,158
Pink
src/app/codelabs/angular/material/material.component.html:165,167
Purple
src/app/codelabs/angular/material/material.component.html:174,177
Read more on theming Angular Material
<a> <a> in this guide</a> </a>
src/app/codelabs/angular/material/material.component.html:182,185
In the next slide there is an exercise. We're going to materialize our
application
src/app/codelabs/angular/material/material.component.html:191,193
Note: the final app won't be super beautiful, we're still working on that.
src/app/codelabs/angular/material/material.component.html:210,211 End of The Material Milestone
src/app/codelabs/angular/material/material.component.html:215,223
Check out Angular Material
<a> <a> "Getting Started"</a> </a>
Guide
src/app/codelabs/angular/material/material.component.html:228,230
Learn how to create simple forms with Angular
src/app/codelabs/angular/forms/forms.component.html:3,5
app.module.ts: Add FormsModule and MatInputModule, to the imports.
src/app/codelabs/angular/forms/forms.component.html:6,9
upload/upload.component.html: Add "title" input bound to the title property of the
component
src/app/codelabs/angular/forms/forms.component.html:10,13
upload/upload.component.html: Add "description" textarea bound to the description
property of the component
src/app/codelabs/angular/forms/forms.component.html:24 Forms
src/app/codelabs/angular/forms/forms.component.html:25 Adding basic forms into your app
src/app/codelabs/angular/forms/forms.component.html:30 src/app/codelabs/angular/forms/forms.component.html:41 Simple Form
src/app/codelabs/angular/forms/forms.component.html:31,34
We have this simple form, let's find out how to map input values with the
ones from our Angular component!
src/app/codelabs/angular/forms/forms.component.html:42 First we have to add <b> <b> FormsModule</b> </b> to our NgModule.
src/app/codelabs/angular/forms/forms.component.html:51 NgModel
src/app/codelabs/angular/forms/forms.component.html:52,55
Now let's use ngModel to bind the inputs to the appropriate fields on the
component.
src/app/codelabs/angular/forms/forms.component.html:56 Try changing the inputs and see the values updated.
src/app/codelabs/angular/forms/forms.component.html:60,63
[(NgModel)] - <b> <b> Banana in the box</b> </b> is a simple mnemonic for the braces
order.
src/app/codelabs/angular/forms/forms.component.html:67 Validation
src/app/codelabs/angular/forms/forms.component.html:68,71
It's really easy to add validation for your inputs using predefined
Angular directives.
src/app/codelabs/angular/forms/forms.component.html:72 Let's make username <b> <b> required</b> </b>
src/app/codelabs/angular/forms/forms.component.html:76,79
If you clear the input it'll be marked with an error, however no error
will be displayed. We'll learn how to display it in the next slide
src/app/codelabs/angular/forms/forms.component.html:83 Displaying Validation Errors
src/app/codelabs/angular/forms/forms.component.html:84 Now we let's display validation error. It takes 2 steps:
src/app/codelabs/angular/forms/forms.component.html:86,88
Get ahold of the input ngModel using <b> <b> #name="ngModel"</b> </b> binding
src/app/codelabs/angular/forms/forms.component.html:89 Use <b> <b> usernameModel</b> </b> 's <b> <b> errors</b> </b> property.
src/app/codelabs/angular/forms/forms.component.html:94,96
Try clearing the username input and see the error displayed.
src/app/codelabs/angular/forms/forms.component.html:99 Validators
src/app/codelabs/angular/forms/forms.component.html:100,106
Here's the list of
<a> <a> built-in validators</a> </a>
that Angular provides out of the box
src/app/codelabs/angular/forms/forms.component.html:108 min
src/app/codelabs/angular/forms/forms.component.html:109 max
src/app/codelabs/angular/forms/forms.component.html:110 required
src/app/codelabs/angular/forms/forms.component.html:111 requiredTrue
src/app/codelabs/angular/forms/forms.component.html:112 email
src/app/codelabs/angular/forms/forms.component.html:113 minLength
src/app/codelabs/angular/forms/forms.component.html:114 maxLength
src/app/codelabs/angular/forms/forms.component.html:115 pattern
src/app/codelabs/angular/forms/forms.component.html:117,123
It's also possible to create custom validators, but it's out of scope for
this milestone, you can read more:
<a> <a> here</a> </a>
src/app/codelabs/angular/forms/forms.component.html:127 Error Always Displayed
src/app/codelabs/angular/forms/forms.component.html:128,131
There's one small issue though, if we don't have initial values, the error
is displayed right away.
src/app/codelabs/angular/forms/forms.component.html:139 Touched & Dirty
src/app/codelabs/angular/forms/forms.component.html:140,143
Luckily we can check if the user interacted with an input using
<b> <b> touched</b> </b> property.
src/app/codelabs/angular/forms/forms.component.html:145,147
<b> <b> dirty</b> </b> is true if the user changed the value of the input.
src/app/codelabs/angular/forms/forms.component.html:148,151
<b> <b> touched</b> </b> is true if the user focused on the input, and then blured
without changing the value.
src/app/codelabs/angular/forms/forms.component.html:156,159
Try focusing/bluring the <b> <b> username</b> </b> field, or adding some value and
then removing to see the error.
src/app/codelabs/angular/forms/forms.component.html:163 Material Inputs (Bonus!)
src/app/codelabs/angular/forms/forms.component.html:164,167
Now let's make our forms beautiful using Material Design inputs! Main
building blocks are:
src/app/codelabs/angular/forms/forms.component.html:169 <b> <b> mat-form-field</b> </b> - Wraps the input
src/app/codelabs/angular/forms/forms.component.html:170 <b> <b> matInput</b> </b> - has to be put on the input
src/app/codelabs/angular/forms/forms.component.html:171 <b> <b> mat-error</b> </b> - Smarter error wrapper
src/app/codelabs/angular/forms/forms.component.html:177,179
Note that we don't need to <b> <b> #name</b> </b> the input anymore.
src/app/codelabs/angular/forms/forms.component.html:184,187
In the next slide there is an exercise. We're going to add a form to the
upload page.
src/app/codelabs/angular/forms/forms.component.html:195,198
Note: you'd have to manually click on the "upload link" to see the result,
we're working on the fix.
src/app/codelabs/angular/forms/forms.component.html:214 End of The Forms Milestone
src/app/codelabs/angular/forms/forms.component.html:218,222
There are more than one way to handle forms in Angular. While
<b> <b> Advanced forms</b> </b> milestone is in works, check out
<a> <a> Angular docs</a> </a>
src/app/codelabs/angular/forms/forms.component.html:225,228
Learn how to start working on your angular app in the Angular-cli
Milestone
src/app/codelabs/angular/angular-cli/angular-cli.component.html:9 Angular-cli
src/app/codelabs/angular/angular-cli/angular-cli.component.html:11 Learn how to quickly start working on your Angular app
src/app/codelabs/angular/angular-cli/angular-cli.component.html:17,20
Angular CLI is a command line tool that can be used to quickly get up to
speed with running your Angular app.
src/app/codelabs/angular/angular-cli/angular-cli.component.html:24,26
Hi, I'm <b> <b> angular-cli</b> </b> , I'm a command line tool!
src/app/codelabs/angular/angular-cli/angular-cli.component.html:27 It may sound scary, but I'm really easy to use!
src/app/codelabs/angular/angular-cli/angular-cli.component.html:33 node
src/app/codelabs/angular/angular-cli/angular-cli.component.html:34 Before you start, make sure you have node.js on your machine.
src/app/codelabs/angular/angular-cli/angular-cli.component.html:35 Open terminal and type in: <code> <code> node -v</code> </code>
src/app/codelabs/angular/angular-cli/angular-cli.component.html:37,40
If there is an error, follow the
<a> <a> NodeJS setup instructions</a> </a>
src/app/codelabs/angular/angular-cli/angular-cli.component.html:41,43
If the output is a number, you're good, move to the next slide.
src/app/codelabs/angular/angular-cli/angular-cli.component.html:51 installing
src/app/codelabs/angular/angular-cli/angular-cli.component.html:52,55
Run <code> <code> npm install -g @angular/cli</code> </code> to install
<b> <b> Angular cli</b> </b> on your machine
src/app/codelabs/angular/angular-cli/angular-cli.component.html:81 Creating new Angular app
src/app/codelabs/angular/angular-cli/angular-cli.component.html:82,85
To create a new Angular app run:
<code> <code> ng new awesome-app</code> </code> , then <code> <code> cd awesome-app</code> </code>
src/app/codelabs/angular/angular-cli/angular-cli.component.html:107 Starting the app
src/app/codelabs/angular/angular-cli/angular-cli.component.html:108,110
Once you're in the app folder, start the app with <code> <code> ng serve</code> </code>
src/app/codelabs/angular/angular-cli/angular-cli.component.html:132,136
Once the app is started just open
<a> <a> http://localhost:4200/</a> </a> in your
browser
src/app/codelabs/angular/angular-cli/angular-cli.component.html:140 Generating components
src/app/codelabs/angular/angular-cli/angular-cli.component.html:141,144
You can easily generate new components
<code> <code> ng generate component my-component</code> </code>
src/app/codelabs/angular/angular-cli/angular-cli.component.html:157 You can also generate modules, services and pipes
src/app/codelabs/angular/angular-cli/angular-cli.component.html:158,160
You can use a shorter version: <code> <code> ng g c my-component</code> </code>
src/app/codelabs/angular/angular-cli/angular-cli.component.html:167,168 End of The Angular-cli Milestone
src/app/codelabs/angular/angular-cli/angular-cli.component.html:171,175
This is <b> <b> the end</b> </b> of the codelab, but it's just the beginning of
your Angular journey. Below are some links that can help you continue
learning.
src/app/codelabs/angular/angular-cli/angular-cli.component.html:179 Find features, docs and events listed here
src/app/codelabs/angular/angular-cli/angular-cli.component.html:183,187
makes it easy to create an application that already works, right
out of the box and generate new components! It also takes care of
the build system for you
================================================
FILE: apps/codelab/src/main.ts
================================================
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { environment } from './environments/environment';
import { AppModule } from './app/app.module';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule);
================================================
FILE: apps/codelab/src/manifest.webmanifest
================================================
{
"name": "Codelab",
"short_name": "Codelab",
"theme_color": "#1976d2",
"background_color": "#fafafa",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "assets/icons/icon-96x96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "assets/icons/icon-128x128.png",
"sizes": "128x128",
"type": "image/png"
},
{
"src": "assets/icons/icon-144x144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "assets/icons/icon-152x152.png",
"sizes": "152x152",
"type": "image/png"
},
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "assets/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
================================================
FILE: apps/codelab/src/polyfills.ts
================================================
import 'fullscreen-api-polyfill';
import 'zone.js/dist/zone'; // Included with Angular CLI.
import '@angular/localize/init';
// Needed for babel :(
(window as any).Buffer = {};
(window as any).process = {
env: { DEBUG: undefined },
argv: {
indexOf() {
return 0;
}
},
getuid() {
return 0;
}
};
================================================
FILE: apps/codelab/src/service-worker.js
================================================
================================================
FILE: apps/codelab/src/styles.scss
================================================
@import '~@angular/material/prebuilt-themes/indigo-pink.css';
body,
html {
margin: 0;
padding: 0;
height: 100%;
}
.presentation.size-600 li {
font-size: 12px;
margin-bottom: 5px;
}
.space-left {
margin-left: 20px;
}
[row] {
flex-direction: row;
display: flex;
}
/* GRID TO USE */
.col-1 {
width: 8.33%;
}
.col-2 {
width: 16.66%;
}
.col-3 {
width: 25%;
}
.col-4 {
width: 33.33%;
}
.col-5 {
width: 41.66%;
}
.col-6 {
width: 50%;
}
.col-7 {
width: 58.33%;
}
.col-8 {
width: 66.66%;
}
.col-9 {
width: 75%;
}
.col-10 {
width: 83.33%;
}
.col-11 {
width: 91.66%;
}
.col-12 {
width: 100%;
}
.presentation.presentation.presentation [no-padding] .slide {
padding: 0;
}
.browser-frame,
.runner,
.browser-frame,
.frame {
}
.slide [large-font] {
font-size: 30px;
}
.slide [p20] {
padding-top: 20px;
}
.slide [m20] {
margin-top: 20px;
}
.slide [m40] {
margin-top: 40px;
}
.slide [column-5] {
width: 50%;
padding: 0 1vw;
}
.slide [column-4] {
width: 40%;
}
.slide [column-2] {
width: 20%;
}
.slide [column-1] {
width: 10%;
}
.slide [flex] {
display: flex;
}
.slide .browser-frame h1 {
font-size: 60px;
}
.slide .browser-frame h2 {
font-size: 20px;
}
/* TODO(kirjs): Support themes */
.slide h1 {
font-size: 20px;
}
.centered-vertically {
display: flex;
flex-direction: column;
flex-grow: 1;
justify-content: center;
}
.popup-message {
padding: 5px 5px 0 5px;
background: black;
color: white;
font-size: 20px;
}
.popup-arrow {
width: 0;
height: 0;
border-left: 0 solid transparent;
border-right: 20px solid transparent;
border-top: 8px solid black;
}
.monaco-editor.vs
.grayed-out-code.grayed-out-code.grayed-out-code.grayed-out-code {
}
.monaco-editor.vs .loc.loc.loc.loc {
background: yellow;
}
.monaco-editor.vs
.highlighted-code.highlighted-code.highlighted-code.highlighted-code {
background: yellow;
}
p {
margin: 0;
margin-bottom: 10px;
padding: 0;
}
.info:before {
content: 'ⓘ';
margin-right: 10px;
}
a {
color: #a03800;
font-weight: 400;
}
.info {
font-weight: 300;
margin-top: 20px;
background: #d3fffd;
padding: 12px;
font-size: 30px;
}
/* BUTTONS BAR CONTAINER */
.btn-bar {
z-index: 2;
position: fixed;
bottom: 1%;
right: 4%;
display: flex;
flex-direction: row;
}
.exercise {
font-size: 30px;
padding: 10px;
margin-top: 20px;
font-weight: 300;
border: 1px #ddd solid;
}
.exercise:before {
content: '💡';
display: inline-block;
width: 20px;
height: 20px;
margin-right: 10px;
}
.exercise.solved {
color: #90dd59;
}
.exercise.solved:before {
content: '✔';
background: none;
}
/* context: https://github.com/Microsoft/monaco-editor/issues/113 */
.monaco-editor .inputarea {
position: fixed !important;
top: 0 !important;
left: 0 !important;
}
.preload {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/*change these sizes to fit into your project*/
width: 100px;
height: 100px;
}
.preload div {
border: 0;
margin: 0;
width: 40%;
height: 40%;
position: absolute;
border-radius: 50%;
animation: spin 2s ease infinite;
}
.preload :first-child {
background: #19a68c;
animation-delay: -1.5s;
}
.preload :nth-child(2) {
background: #f63d3a;
animation-delay: -1s;
}
.preload :nth-child(3) {
background: #fda543;
animation-delay: -0.5s;
}
.preload :last-child {
background: #193b48;
}
@keyframes spin {
0%,
100% {
transform: translate(0);
}
25% {
transform: translate(160%);
}
50% {
transform: translate(160%, 160%);
}
75% {
transform: translate(0, 160%);
}
}
.mt-60 {
margin-top: 60px;
}
================================================
FILE: apps/codelab/src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/long-stack-trace-zone';
import 'zone.js/dist/proxy.js';
import 'zone.js/dist/sync-test';
import 'zone.js/dist/jasmine-patch';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async-test';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
declare var require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function() {};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
// Finally, start Karma to run the tests.
__karma__.start();
================================================
FILE: apps/codelab/src/typings.d.ts
================================================
/* SystemJS module definition */
declare var module: NodeModule;
declare const chai;
interface NodeModule {
id: string;
}
================================================
FILE: apps/codelab/tsconfig.app.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": []
},
"files": ["./src/main.ts", "./src/polyfills.ts"],
"exclude": ["test.ts", "**/*.spec.ts"]
}
================================================
FILE: apps/codelab/tsconfig.json
================================================
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"types": ["jasmine", "node"]
},
"angularCompilerOptions": {
"enableIvy": true
}
}
================================================
FILE: apps/codelab/tsconfig.spec.json
================================================
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["jasmine", "node"]
},
"files": ["src/test.ts", "src/polyfills.ts"],
"include": ["**/*.spec.ts", "**/*.d.ts"]
}
================================================
FILE: apps/codelab/tslint.json
================================================
{
"extends": "../../tslint.json",
"linterOptions": {
"exclude": ["src/**/*.json"]
},
"rules": {}
}
================================================
FILE: apps/kirjs/browserslist
================================================
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed
> 0.5%
last 2 versions
Firefox ESR
not dead
not IE 9-11
================================================
FILE: apps/kirjs/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
const { join } = require('path');
const getBaseKarmaConfig = require('../../karma.conf');
module.exports = function(config) {
const baseConfig = getBaseKarmaConfig();
config.set({
...baseConfig,
coverageIstanbulReporter: {
...baseConfig.coverageIstanbulReporter,
dir: join(__dirname, '../../coverage/apps/kirjs')
}
});
};
================================================
FILE: apps/kirjs/src/app/app.component.css
================================================
.fire:hover {
opacity: 1;
cursor: pointer;
background: #fff;
box-shadow: 0 0 2px #333333;
}
.fire {
opacity: 0.8;
position: fixed;
right: 10px;
top: 10px;
background: #ddd;
border-radius: 50%;
width: 20px;
height: 20px;
font-size: 12px;
text-decoration: none;
text-align: center;
text-indent: 3px;
z-index: 100;
}
================================================
FILE: apps/kirjs/src/app/app.component.html
================================================
================================================
FILE: apps/kirjs/src/app/app.component.spec.ts
================================================
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent]
}).compileComponents();
}));
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'kirjs'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('kirjs');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain(
'Welcome to kirjs!'
);
}));
});
================================================
FILE: apps/kirjs/src/app/app.component.ts
================================================
import { Component, HostListener } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'kirjs-main',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'kirjs';
@HostListener(`window:keydown.meta.'`)
HandleLinker() {
const key = 'linker';
if (localStorage.getItem(key)) {
localStorage.removeItem(key);
alert(`Linker removed!`);
} else {
const path = window.location.pathname;
localStorage.setItem(key, path);
alert(`Linker stored ${path}!`);
}
}
constructor(private router: Router) {
const path: string = localStorage.getItem('linker');
if (path && path !== window.location.pathname) {
router.navigate([path]);
}
}
}
================================================
FILE: apps/kirjs/src/app/cv/resume.css
================================================
ul {
margin: 0;
padding: 0;
list-style: none;
}
ul li {
margin: 0;
padding: 0;
}
/*.......| Resume |.......................*/
#resume {
max-width: 800px;
margin: 0 auto;
font-family: verdana, sans-serif;
}
#resume #header {
width: 250px;
float: left;
}
#resume #header .title {
width: 90px;
}
#resume #info {
margin-left: 300px;
}
#resume h1 {
font-size: 1.4em;
text-transform: uppercase;
margin-top: 30px;
margin-bottom: 20px;
}
#resume h2 {
color: #900;
font-size: 1.4em;
font-weight: 400;
padding-top: 0px;
padding-bottom: 10px;
margin-bottom: 0;
}
#resume h3 {
margin: 0;
font-size: 0.8em;
font-weight: 600;
}
#resume h4 {
font-size: 0.8em;
color: #444;
font-weight: 400;
padding: 0;
margin: 0;
}
#resume a {
color: #990000;
}
#resume .line {
font-size: 0.8em;
overflow: hidden;
padding-top: 12px;
}
#resume .title {
color: #444;
width: 130px;
display: block;
float: left;
}
#resume dl {
line-height: 30px;
}
#resume dt {
width: 190px;
float: left;
}
#resume .spanstart:after {
content: ' - ';
}
#resume .date_duration {
font-weight: 400;
}
#resume .vcard > h1 {
font-size: 1.6em;
padding: 0;
margin: 0;
font-weight: 400;
padding-bottom: 10px;
}
#resume .experience {
padding-bottom: 20px;
}
#resume .experience:last-child {
padding-bottom: 0;
}
#resume .experience-header {
overflow: hidden;
}
#resume .experience-hgroup {
float: left;
}
#resume .vcalendar li {
font-size: 0.8em;
}
#resume .description {
list-style: circle;
color: #666;
margin-left: 20px;
padding-left: 20px;
line-height: 25px;
}
#resume .skills {
overflow: hidden;
margin: 0;
}
#resume .skills li {
float: left;
}
================================================
FILE: apps/kirjs/src/app/cv/resume.html
================================================
Designed and contributed to the development of an Angular single
page app for generating reports
Developed Backbone-based form handling framework, for a CRUD
Single Page application
Organized and led internal hackathons, debates and presentations
Provided IT support for 50 users
Created a single page Backbone app, utilizing YouTube API
Developed Active Directory management system with C#
Developed WordPress web sites for CIPE, ALA group, Taxpayers for
Common Sense, Rosebar Lounge.
================================================
FILE: apps/kirjs/src/app/cv/resume.scss
================================================
ul {
li {
margin: 0;
padding: 0;
}
margin: 0;
padding: 0;
list-style: none;
}
/*.......| Resume |.......................*/
#resume {
#header {
width: 250px;
float: left;
.title {
width: 90px;
}
}
#info {
margin-left: 300px;
}
h1 {
font-size: 1.4em;
text-transform: uppercase;
margin-top: 30px;
margin-bottom: 20px;
}
h2 {
color: #900;
font-size: 1.4em;
font-weight: 400;
padding-top: 0px;
padding-bottom: 10px;
margin-bottom: 0;
}
h3 {
margin: 0;
font-size: 0.8em;
font-weight: 600;
}
h4 {
font-size: 0.8em;
color: #444;
font-weight: 400;
padding: 0;
margin: 0;
}
a {
color: #990000;
}
.line {
font-size: 0.8em;
overflow: hidden;
padding-top: 12px;
}
.title {
color: #444;
width: 130px;
display: block;
float: left;
}
max-width: 800px;
margin: 0 auto;
font-family: verdana, sans-serif;
dl {
line-height: 30px;
}
dt {
width: 190px;
float: left;
}
.spanstart:after {
content: ' - ';
}
.date_duration {
font-weight: 400;
}
.vcard > h1 {
font-size: 1.6em;
padding: 0;
margin: 0;
font-weight: 400;
padding-bottom: 10px;
}
.experience {
&:last-child {
padding-bottom: 0;
}
padding-bottom: 20px;
}
.experience-header {
overflow: hidden;
}
.experience-hgroup {
float: left;
}
.vcalendar li {
font-size: 0.8em;
}
.description {
list-style: circle;
color: #666;
margin-left: 20px;
padding-left: 20px;
line-height: 25px;
}
.skills {
overflow: hidden;
margin: 0;
li {
float: left;
}
}
}
================================================
FILE: apps/kirjs/src/app/kirjs.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { monacoReady } from '@codelab/code-demos';
import { AppComponent } from './app.component';
import { RouterModule } from '@angular/router';
import { environment } from '../../../codelab/src/environments/environment';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { OverlayComponent } from './modules/streaming/overlay/overlay.component';
import { MatCardModule } from '@angular/material/card';
import { MarkdownModule } from 'ngx-markdown';
export const angularFire = AngularFireModule.initializeApp(
environment.firebaseConfig
);
const routes = [
{
path: 'binary',
loadChildren: () =>
import('./modules/binary/binary.module').then(m => m.BinaryModule),
name: 'Binary',
description: 'Learn about Binary in JS',
page: 'bonus',
prod: true
},
{
path: 'gomoku',
loadChildren: () =>
import('./modules/gomoku/gomoku.module').then(m => m.GomokuModule),
name: 'Gomoku',
description: 'Gomoku',
page: 'bonus',
prod: true
},
{
path: 'cellular-automation',
loadChildren: () =>
import(
'./modules/cellular-automation/cellular-automation-routing.module'
).then(m => m.CellularAutomationRoutingModule),
name: 'Image inclusion',
description: 'Image inclusion'
},
{
path: 'music',
loadChildren: () =>
import('./modules/music/music.module').then(m => m.MusicModule),
name: 'Music',
description: 'Music'
},
{
path: 'webassembly',
loadChildren: () =>
import('./modules/webassembly/webassembly.module').then(
m => m.WebassemblyModule
),
name: 'webassembly',
description: 'webassembly'
},
{
path: 'svg',
loadChildren: () =>
import('./modules/svg/svg.module').then(m => m.SvgModule),
name: 'Svg + Angular',
description: 'SVG '
},
{
path: 'regex',
loadChildren: () =>
import('./modules/regex/regex.module').then(m => m.RegexModule),
name: 'Regex',
description: 'Regex '
},
{
path: 'ast',
loadChildren: () =>
import('./modules/ast/ast.module').then(m => m.AstModule),
name: 'Ast + Angular',
description: 'SVG '
},
{
path: 'svg-race',
loadChildren: () =>
import('./modules/svg-race/svg-race.module').then(m => m.SvgRaceModule),
name: 'SVG Race',
description: 'SVG '
},
{
path: '',
loadChildren: () =>
import('./modules/home/home.module').then(m => m.HomeModule),
name: 'Home',
description: 'Home'
},
{
path: 'streaming',
loadChildren: () =>
import('./modules/streaming/streaming.module').then(
m => m.StreamingModule
),
name: 'Home',
description: 'Home'
},
{
path: 'sync',
loadChildren: () =>
import('./modules/sync/sync.module').then(m => m.SyncModule),
name: 'Sync',
description: 'Sync Session'
},
{
path: 'stack',
loadChildren: () =>
import('./modules/stack/stack.module').then(m => m.StackModule),
name: 'Stack Module',
description: 'stack'
},
{
path: 'sync',
loadChildren: () =>
import('./modules/sync/sync.module').then(m => m.SyncModule),
name: 'Sync',
description: 'Sync Session'
},
{
path: 'test',
loadChildren: () =>
import('./modules/test/test.module').then(m => m.TestModule),
name: 'Home',
description: 'Home'
},
{
path: 'qna',
loadChildren: () =>
import('./modules/qna/qna.module').then(m => m.QnaModule),
name: 'Q&A'
},
{
path: 'msk',
loadChildren: () =>
import('./modules/msk/msk.module').then(m => m.MskModule),
name: 'Angular Moscow Meetup'
},
{
path: 'stack',
loadChildren: () =>
import('./modules/stack/stack-routing.module').then(
m => m.StackRoutingModule
),
name: 'Stack Module',
description: 'stack'
}
];
@NgModule({
declarations: [AppComponent, OverlayComponent],
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot(routes),
AngularFireAuthModule,
AngularFireDatabaseModule,
angularFire,
MatCardModule,
MarkdownModule
],
providers: [
{
provide: APP_INITIALIZER,
useValue: monacoReady,
multi: true
},
{
provide: 'ROUTES',
useValue: []
},
{
provide: APP_INITIALIZER,
useValue: monacoReady,
multi: true
}
],
bootstrap: [AppComponent]
})
export class KirjsModule {}
================================================
FILE: apps/kirjs/src/app/modules/ast/ast-preview-runner/ast-preview-runner.component.css
================================================
.error {
color: red;
font-size: 50px;
}
:host ::ng-deep .header.header.header.header {
font-size: inherit;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/ast-preview-runner/ast-preview-runner.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/ast/ast-preview-runner/ast-preview-runner.component.ts
================================================
import {
Component,
EventEmitter,
Input,
OnChanges,
Output,
SimpleChanges
} from '@angular/core';
import { parse } from 'babylon';
declare const require;
@Component({
selector: 'kirjs-ast-preview-runner',
templateUrl: './ast-preview-runner.component.html',
styleUrls: ['./ast-preview-runner.component.css']
})
export class AstPreviewRunnerComponent implements OnChanges {
ast: any;
hasError = false;
@Input() program = true;
@Input() code = '';
@Output() highlight = new EventEmitter();
update() {
this.run();
}
selectNode({ loc }) {
this.highlight.emit([
loc.start.line,
loc.start.column + 1,
loc.end.line,
loc.end.column + 1
]);
}
run() {
this.hasError = false;
try {
this.ast = parse(this.code);
if (this.program) {
this.ast = this.ast.program.body;
}
} catch (e) {
this.hasError = true;
}
}
ngOnChanges(changes: SimpleChanges) {
this.run();
}
constructor() {}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/ast-preview-runner/ast-preview-runner.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AstPreviewRunnerComponent } from './ast-preview-runner.component';
import { FormsModule } from '@angular/forms';
import { AngularAstVizModule } from '@codelab/angular-ast-viz';
@NgModule({
imports: [CommonModule, FormsModule, AngularAstVizModule],
declarations: [AstPreviewRunnerComponent],
exports: [AstPreviewRunnerComponent]
})
export class AstPreviewRunnerModule {}
================================================
FILE: apps/kirjs/src/app/modules/ast/ast.component.css
================================================
.bg {
background: #fff;
opacity: 0.7;
padding: 2vw 5vw;
margin: 5vw 0;
border: 1px solid #000;
}
#intro h1 {
font-size: 9vw;
font-weight: 100;
font-family: 'Helvetica Neue', sans-serif;
}
a {
color: #9c2600;
}
#intro h2,
#outro h2 {
font-size: 12vw;
}
#intro h2,
#outro h1 {
font-size: 6vw;
}
:host /deep/ .slide > div {
width: 100%;
}
:host /deep/ .slide {
background: transparent;
}
#sausage-pre {
background: url(pics/sausage-pre.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#not-like-sausage {
background: url(pics/sausage2.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#babel-types {
background: url(pics/park.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#babel-traverse .bg,
#babel-transform .bg,
#renoir .bg {
margin: 30vw 0;
}
#babel-traverse {
background: url(pics/girl-reading-1890.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#babel-transform {
background: url(pics/doggie.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#debugger-pre {
background: url(pics/debugger.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#renoir {
background: url(pics/renoir.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#babel-remove-debugger {
background: url(pics/cat.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#babel-fit {
background: url(pics/the-garden.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#parser {
background: url(pics/parser.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#links-5-cool-things {
background: url(pics/bg-links.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#panda {
background: url(pics/panda.webp) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
.henry-zhu {
background: url(pics/patreon.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
width: 70vw;
}
.babylon {
background: url(pics/babylon.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
width: 70vw;
}
.babylon {
background: url(pics/babylon.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
width: 70vw;
}
.success-complete-disco {
margin-top: 20px;
width: 500px;
height: 300px;
background: url(pics/success/disco.webp);
background-repeat: no-repeat;
background-size: cover;
}
.success-complete-husky {
margin-top: 20px;
width: 500px;
height: 300px;
background: url(pics/success/happy.webp);
background-repeat: no-repeat;
background-size: cover;
}
.success-complete-dancing {
margin-top: 20px;
width: 500px;
height: 300px;
background: url(pics/success/dancing-dog.webp);
background-repeat: no-repeat;
background-size: cover;
}
.github {
background: url(pics/github.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
width: 70vw;
}
.opencollective {
background: url(pics/opencollective.png) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
width: 70vw;
}
#eslint {
background: url(pics/debugger.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
}
#kirjs {
background: url(pics/kirjs.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
}
#intro,
#outro {
background: url(pics/landscape-with-trees-1.jpg) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
}
.bad-tree {
background: url(pics/tree.gif) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
}
#venok img {
width: 384px;
height: 288px;
margin: 0 auto;
}
.ast-explorer-link:hover {
font-size: 5vw;
}
.ast-explorer-link {
padding-top: 25px;
font-size: 10px;
}
.description.description.description {
margin: 0;
margin-top: 20px;
padding: 0;
font-size: 0;
}
#debugger-regex /deep/ .solution:hover:before {
color: red;
font-size: 3vw;
}
:host /deep/ #debugger-regex .solution {
}
.ast-preview-helper-list li:hover {
color: #444;
}
.ast-preview-helper-list li {
font-size: 20px;
text-decoration: dashed;
cursor: pointer;
color: #999;
border-bottom: 1px #ddd dashed;
}
.ast-preview {
font-size: 30px;
}
.two-button {
background: url(pics/button-1.png) no-repeat;
background-size: 50% auto;
display: inline-block;
height: 50vh;
font-size: 5vw;
border: 0 solid;
padding: 0;
margin: 0;
}
.two-button2 {
background: url(pics/two-button2.gif) no-repeat;
background-size: 50% auto;
display: inline-block;
height: 50vh;
font-size: 5vw;
border: 0 solid;
padding: 0;
margin: 0;
}
.two-button3 {
background: url(pics/two-button3.gif) no-repeat;
background-size: 100% auto;
display: inline-block;
height: 100vh;
font-size: 5vw;
border: 0 solid;
padding: 0;
margin: 0;
}
.link-number {
display: inline-block;
font-size: 8vw;
border-radius: 50%;
width: 10vw;
height: 10vw;
text-align: center;
margin-bottom: 10vh;
}
.collie.display {
background: url(pics/collie.webp) no-repeat;
}
.collie {
background-size: 100% auto;
display: inline-block;
height: 40vh;
font-size: 5vw;
width: 50vw;
}
.btn-bar {
line-height: 3vw;
}
.btn-bar:hover .font-size {
display: block;
font-size: 4vw;
}
.btn-bar .font-size {
display: none;
}
.twitter {
color: #444;
font-size: 3vw;
margin: 2vw;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/ast.component.html
================================================
Абстрактные Синтаксические Деревья в JavaScript
@kirjs
Абстрактные синтаксические деревья (АСД)
Это выступление о том как научить код понимать и
трансформировать другой код.
Начнем сразу с примера
Как определить что из файла забыли вырезать console.log
Парсим с помощью babylon:
19 Мая babylon был переименован в @babel/parser
Пример Абстрактного Синтаксического Дерева:
log
console.log
log()
console.log()
console.log(1)
console.log(1, a.b)
All literals
Обход AST с помощью babel-traverse
Упрощаем работу с AST с помощью babel-types
Давайте найдем в коде debugger
Где еще полезны АСД?
Больше контроля надо кодом, проще рефакторинг, codemods
Меньше споров про оформление кода
Создание игровых проэктов
Лучшее понимание JavaScript
Когда вы увидите эти 5 ссылок про Babel вы не поверите своим
глаза...
2
Часть процесса добавления новой функциональности в EcmaScript - создание
плагина для Babel.
3
Babel Generator теряет форматирование при гегенерации кода.
4
Оргомная подборка информации про Babel в репозитории
Awesome Babel
5
Babel - проект с открытыми исходниками, над ним трудится команда
волонтеров и им не помешала бы помощь :)
================================================
FILE: apps/kirjs/src/app/modules/ast/ast.component.ts
================================================
import { Component } from '@angular/core';
import { parse } from 'babylon';
import {
findHighlightsObjectProp,
isBody,
isLoc,
isType,
processCode,
removeDoubleWhiteLines,
removeLoc
} from './parse-hello-world-ast';
import { exercise } from '../../../../../codelab/src/app/shared/helpers/helpers';
declare const require;
const helloWorld = require('./samples/hello-world.json');
function jsify(program) {
return 'const ast = ' + JSON.stringify(program, null, ' ');
}
const helloWorldCodePre = jsify(helloWorld);
const angularCodeBefore = `import {
Component
} from '@angular/core';
@Component({
selector: 'amazing-component',
template: './app.html',
})
export class SimpleEditorComponent implements ControlValueAccessor {
}`;
const angularCodeAfter = `import {
Component,
forwardRef,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'amazing-component',
template: './app.html',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SimpleEditorComponent),
multi: true
}
],
})
export class SimpleEditorComponent implements ControlValueAccessor {
registerOnTouched(fn: any): void {
}
registerOnChange(onChange: (code: string) => void): void {
this.change.subscribe(onChange)
}
writeValue(value: string): void {
console.log(value)
}
}`;
const consoleLog = `console.log('hello')`;
const consoleLogAst = parse(consoleLog);
const consoleLogCode = JSON.stringify(consoleLogAst, null, ' ');
const consoleLogBodyAst = consoleLogAst.program.body;
const consoleLogBodyAstCode = JSON.stringify(consoleLogBodyAst, null, ' ');
const consoleLogNoLocCode = removeDoubleWhiteLines(
processCode(consoleLogBodyAstCode, { remove: [removeLoc] })
);
@Component({
selector: 'kirjs-ast',
templateUrl: './ast.component.html',
styleUrls: ['./ast.component.css']
})
export class AstComponent {
fontSize = 28;
displayCallee = false;
code = {
parser: `import { parse } from 'babylon';
parse("console.log('🐶🐶🐶')");`,
astPreview: 'log',
astPreviewDebugger: 'console.log();\ndebugger\n123',
types: {
identifier: `
// Before
path.node.property.type === 'Identifier'
// with babel types
import {isIdentifier} from 'babel-types';
isIdentifier(path.node.property)`,
name: `
// Before
path.node.property.type === 'Identifier'
path.node.property.name === 'log'
// with babel types
import {isIdentifier} from 'babel-types';
isIdentifier(path.node.property, {name: log})`
},
astHello: 'hello(console.log)',
matches: {
loc: /"loc": \{[\s\S]*?\{[\s\S]*?\}[\s\S]*?\{[\s\S]*?\}[\s\S]*?\},/
},
astExampleFull: processCode(helloWorldCodePre, { remove: [] }),
astExample: removeDoubleWhiteLines(
processCode(helloWorldCodePre, { remove: [removeLoc] })
),
astExampleNoBody: removeDoubleWhiteLines(
processCode(jsify(helloWorld.program.body), { remove: [removeLoc] })
),
consoleLog: consoleLog,
consoleLogAst: consoleLogCode,
consoleLogAstJs: 'const ast = ' + consoleLogCode,
consoleLogBodyAstCode,
consoleLogNoLocCode,
angularCodeBefore,
angularCodeAfter,
angularMatchesAfter: [/a/],
findConsoleLog: [
exercise(
'find-console-log',
require('!!raw-loader!./samples/find-console-log/find-console-log.js'),
require('!!raw-loader!./samples/find-console-log/find-console-log-regex.solved.js')
),
exercise(
'find-console-log.test',
require('!!raw-loader!./samples/find-console-log/find-console-log.test.js')
)
],
findDebuggerBabel: [
exercise(
'find-debugger-babel',
require('!!raw-loader!./samples/find-debugger/find-debugger-babel.ts'),
require('!!raw-loader!./samples/find-debugger/find-debugger-babel.solved.ts')
),
exercise(
'find-debugger.test',
require('!!raw-loader!./samples/find-debugger/find-debugger.test.js')
)
],
traverseConsoleLogBabel: [
exercise(
'traverse-console-log-babel',
require('!!raw-loader!./samples/find-console-log/traverse-console-log-babel.ts'),
require('!!raw-loader!./samples/find-console-log/traverse-console-log-babel.solved.ts')
),
exercise(
'find-console-log.test',
require('!!raw-loader!./samples/find-console-log/find-console-log.test.js')
)
],
traverseConsoleLogBabel2: [
exercise(
'traverse-console-log-babel',
require('!!raw-loader!./samples/find-console-log/traverse-console-log-babel.solved.ts'),
require('!!raw-loader!./samples/find-console-log/traverse-console-log-babel.solved2.ts')
),
exercise(
'find-console-log.test',
require('!!raw-loader!./samples/find-console-log/find-console-log.test.js')
)
],
removeConsoleLogBabel: [
exercise(
'remove-console-log',
require('!!raw-loader!./samples/find-console-log/remove-console-log.ts'),
require('!!raw-loader!./samples/find-console-log/remove-console-log.solved.ts')
),
exercise(
'remove-console-log.test',
require('!!raw-loader!./samples/find-console-log/remove-console-log.test.js')
)
],
findfIt: [
exercise(
'find-fit',
require('!!raw-loader!./samples/find-fit/find-fit.js'),
require('!!raw-loader!./samples/find-fit/find-fit.solved.js')
),
exercise(
'find-fit.test',
require('!!raw-loader!./samples/find-fit/find-fit.test.js')
)
],
itLines: [
exercise(
'it-lines',
require('!!raw-loader!./samples/it-lines/it-lines.js'),
require('!!raw-loader!./samples/it-lines/it-lines.solved.js')
),
exercise(
'it-lines.test',
require('!!raw-loader!./samples/it-lines/it-lines.test.js')
)
],
decToBin: require('!!raw-loader!./samples/dec-to-bin.js'),
decToBinFixed: require('!!raw-loader!./samples/dec-to-bin-with-semicolons.js')
};
constructor() {}
matchTreePartsLoc(code) {
return findHighlightsObjectProp(code, [isLoc]);
}
matchTreePartsBody(code) {
return findHighlightsObjectProp(code, [isBody]);
}
matchTreePartsType(code) {
return findHighlightsObjectProp(code, [isType]);
}
updateFontSize(diff) {
this.fontSize += diff;
}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/ast.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { MatCardModule } from '@angular/material/card';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { AngularSlidesToPdfModule } from '@codelab/angular-slides-to-pdf';
import { BrowserWindowModule } from '@codelab/browser';
import { FeedbackModule } from '@codelab/feedback';
import { CodeDemoModule } from '@codelab/code-demos';
import { AstPreviewRunnerModule } from './ast-preview-runner/ast-preview-runner.module';
import { NewProgressBarModule } from './new-progress-bar/new-progress-bar.module';
import { AstComponent } from './ast.component';
import { DebuggerComponent } from './debugger/debugger.component';
import { TestSetComponent } from './test-set/test-set.component';
import { SizePickerModule } from './size-picker/size-picker.module';
import { BabelTestRunnerComponent } from './test-set/babel-test-runner/babel-test-runner.component';
const routes = RouterModule.forChild(SlidesRoutes.get(AstComponent));
@NgModule({
imports: [
routes,
CommonModule,
AstPreviewRunnerModule,
SlidesModule,
BrowserWindowModule,
FeedbackModule,
CodeDemoModule,
FormsModule,
SizePickerModule,
MatCardModule,
NewProgressBarModule,
AngularSlidesToPdfModule
],
declarations: [
AstComponent,
BabelTestRunnerComponent,
DebuggerComponent,
TestSetComponent
],
exports: [AstComponent]
})
export class AstModule {}
================================================
FILE: apps/kirjs/src/app/modules/ast/babel-highlight/babel-highlight-match.directive.ts
================================================
import { AfterViewInit, Directive, Input, NgModule } from '@angular/core';
import { CodeDemoEditorComponent } from '@codelab/code-demos/src/lib/code-demo-editor/code-demo-editor.component';
// TODO(kirjs): Uncommit
@Directive({
// tslint:disable-next-line:all TODO: Fix linter warnings on the selector and delete this comment.
selector: '[slidesBabelHighlightMatch]'
})
export class BabelHighlightDirective implements AfterViewInit {
// tslint:disable-next-line:all TODO: Fix linter warnings on the next line and delete this comment.
@Input('slidesBabelHighlightMatch') callback;
constructor(private editorComponent: CodeDemoEditorComponent) {}
ngAfterViewInit(): void {
if (this.callback) {
const matches = this.callback(this.editorComponent.code) || [];
const decorations = matches.map(match => {
return {
range: new this.editorComponent.monacoConfigService.monaco.Range(
...match.loc
),
options: { inlineClassName: match.className }
};
});
this.editorComponent.editor.deltaDecorations([], decorations);
}
}
}
@NgModule({
declarations: [BabelHighlightDirective]
})
export class MockaADASDASDASDASDASDModule {}
================================================
FILE: apps/kirjs/src/app/modules/ast/debugger/debugger.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/ast/debugger/debugger.component.html
================================================
RUN!
================================================
FILE: apps/kirjs/src/app/modules/ast/debugger/debugger.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebuggerComponent } from './debugger.component';
describe('DebuggerComponent', () => {
let component: DebuggerComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DebuggerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DebuggerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/ast/debugger/debugger.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
declare const require;
const code = require('!!raw-loader!./debugger.ts');
@Component({
selector: 'kirjs-debugger-sample',
templateUrl: './debugger.component.html',
styleUrls: ['./debugger.component.css']
})
export class DebuggerComponent implements OnInit {
@Input() fontSize = 20;
code = code;
constructor() {}
run() {
eval(this.code + ';weird()');
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/debugger/debugger.ts
================================================
function weird() {
parseInt('Infinity', 10);
parseInt('Infinity', 18);
parseInt('Infinity', 19);
parseInt('Infinity', 23);
parseInt('Infinity', 24);
parseInt('Infinity', 29);
parseInt('Infinity', 30);
parseInt('Infinity', 34);
parseInt('Infinity', 35);
parseInt('Infinity', 36);
parseInt('Infinity', 37);
}
================================================
FILE: apps/kirjs/src/app/modules/ast/new-progress-bar/new-progress-bar.component.css
================================================
.progress-bar {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
z-index: 100;
background-color: #fff;
border-top: 1px solid #000;
padding: 0.5vw;
opacity: 0.3;
}
.progress-bar:hover .font-size {
visibility: visible;
}
.progress-bar:hover {
opacity: 1;
}
.font-size {
visibility: hidden;
}
.slide-block.completed-slide {
background: #ddd;
}
.slide-block.completed-slide {
background: #000;
}
.slide-block:hover {
opacity: 1;
cursor: pointer;
}
.slide-block.current-slide {
background: lime;
}
.slide-block {
margin-top: 0.1vw;
opacity: 0.7;
width: 0.5vw;
height: 0.5vw;
border: 1px solid #000;
border-radius: 50%;
margin-right: 0.3vw;
}
.handle {
font-family: 'Helvetica Neue', sans-serif;
position: fixed;
right: 0;
bottom: 0;
color: #444444;
font-size: 3vw;
z-index: 101;
padding: 0.5vw;
padding-right: 2vw;
text-shadow: 0px 3px 20px #fff, 0px -3px 20px #fff, -3px 0px 20px #fff,
3px 0px 20px #fff;
text-decoration: none;
}
kirjs-size-picker {
margin-right: 200px;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/new-progress-bar/new-progress-bar.component.html
================================================
sli.do/armadajs | @kirjs
================================================
FILE: apps/kirjs/src/app/modules/ast/new-progress-bar/new-progress-bar.component.ts
================================================
import {
AfterViewInit,
Component,
EventEmitter,
Input,
Output
} from '@angular/core';
import { SlidesDeckComponent } from '@ng360/slides';
@Component({
selector: 'kirjs-new-progress-bar',
templateUrl: './new-progress-bar.component.html',
styleUrls: ['./new-progress-bar.component.css']
})
export class NewProgressBarComponent implements AfterViewInit {
@Input() fontSize = 28;
@Input() title = 'JavaScript AST';
@Output() fontSizeChange = new EventEmitter();
slides = [];
activeSlideIndex = 0;
tempSlideId = 0;
constructor(public deck: SlidesDeckComponent) {}
ngAfterViewInit() {
// Change detection complains if updating it right away.
requestAnimationFrame(() => {
this.slides = this.deck.slides;
this.activeSlideIndex = this.deck.activeSlideIndex;
});
this.deck.slideChange.subscribe(index => {
this.activeSlideIndex = index;
});
}
previewSlide(index) {
this.tempSlideId = this.activeSlideIndex;
this.deck.goToSlide(index);
}
goToSlide(index) {
this.deck.goToSlide(index);
this.tempSlideId = this.activeSlideIndex;
}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/new-progress-bar/new-progress-bar.module.ts
================================================
import { NgModule } from '@angular/core';
import { NewProgressBarComponent } from './new-progress-bar.component';
import { CommonModule } from '@angular/common';
import { SizePickerModule } from '../size-picker/size-picker.module';
@NgModule({
imports: [CommonModule, SizePickerModule],
declarations: [NewProgressBarComponent],
exports: [NewProgressBarComponent]
})
export class NewProgressBarModule {}
================================================
FILE: apps/kirjs/src/app/modules/ast/parse-hello-world-ast.ts
================================================
import { parse } from 'babylon';
import generate from '@babel/generator';
import babel_traverse from '@babel/traverse';
export function isLoc(node) {
return (
node.key.value === 'loc' ||
node.key.value === 'start' ||
node.key.value === 'end' ||
node.key.value === 'extra'
);
}
export function isType(node) {
return node.key.value === 'type';
}
export function isBody(node) {
return node.key.value === 'body';
}
export function removeLoc(path) {
if (isLoc(path.node)) {
path.remove();
}
}
export function removeExtra(path) {
if (isLoc(path.node)) {
path.remove();
}
}
export function locToMonacoLoc(loc, className) {
return {
loc: [
loc.start.line,
loc.start.column + 1,
loc.end.line,
loc.end.column + 1
],
className
};
}
export function parseCode(code) {
return parse(code);
}
export function processCode(code, { remove = [] }: any) {
const ast = parse(code, {
sourceType: 'module',
plugins: ['decorators']
});
babel_traverse(ast, {
ObjectProperty(path) {
remove.forEach(callback => callback(path));
}
});
return generate(ast, {}).code;
}
export function findHighlightsObjectProp(code: string, matchers: Array) {
const ast = parse(code, {
sourceType: 'module',
plugins: ['decorators']
});
const highlights = [];
babel_traverse(ast, {
ObjectProperty({ node }) {
if (matchers.some(matcher => matcher(node))) {
highlights.push(locToMonacoLoc(node.loc, 'loc'));
}
}
});
return highlights;
}
export function findHighlightsAll(code: string, matcher) {
const ast = parse(code, {
sourceType: 'module',
plugins: ['decorators']
});
const highlights = [];
babel_traverse(ast, {
enter({ node }) {
const className = matcher(node);
if (className) {
highlights.push(locToMonacoLoc(node.loc, className));
}
}
});
return highlights;
}
export function removeDoubleWhiteLines(code) {
return code.replace(/\n\n/gi, '\n');
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/dec-to-bin-with-semicolons.js
================================================
function bin6BitsToDec(bin) {
switch (bin) {
case '000000':
return 0;
case '000001':
return 1;
case '000010':
return 2;
case '000011':
return 3;
case '000100':
return 4;
case '000101':
return 5;
case '000110':
return 6;
case '000111':
return 7;
case '001000':
return 8;
case '001001':
return 9;
case '001010':
return 10;
case '001011':
return 11;
case '001100':
return 12;
case '001101':
return 13;
case '001110':
return 14;
case '001111':
return 15;
case '010000':
return 16;
case '010001':
return 17;
case '010010':
return 18;
case '010011':
return 19;
case '010100':
return 20;
case '010101':
return 21;
case '010110':
return 22;
case '010111':
return 23;
case '011000':
return 24;
case '011001':
return 25;
case '011010':
return 26;
case '011011':
return 27;
case '011100':
return 28;
case '011101':
return 29;
case '011110':
return 30;
case '011111':
return 31;
case '100000':
return 32;
case '100001':
return 33;
case '100010':
return 34;
case '100011':
return 35;
case '100100':
return 36;
case '100101':
return 37;
case '100110':
return 38;
case '100111':
return 39;
case '101000':
return 40;
case '101001':
return 41;
case '101010':
return 42;
case '101011':
return 43;
case '101100':
return 44;
case '101101':
return 45;
case '101110':
return 46;
case '101111':
return 47;
case '110000':
return 48;
case '110001':
return 49;
case '110010':
return 50;
case '110011':
return 51;
case '110100':
return 52;
case '110101':
return 53;
case '110110':
return 54;
case '110111':
return 55;
case '111000':
return 56;
case '111001':
return 57;
case '111010':
return 58;
case '111011':
return 59;
case '111100':
return 60;
case '111101':
return 61;
case '111110':
return 62;
case '111111':
return 63;
}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/dec-to-bin.js
================================================
function bin6BitsToDec(bin) {
switch (bin) {
case '000000':
return 0;
case '000001':
return 1;
case '000010':
return 2;
case '000011':
return 3;
case '000100':
return 4;
case '000101':
return 5;
case '000110':
return 6;
case '000111':
return 7;
case '001000':
return 8;
case '001001':
return 9;
case '001010':
return 10;
case '001011':
return 11;
case '001100':
return 12;
case '001101':
return 13;
case '001110':
return 14;
case '001111':
return 15;
case '010000':
return 16;
case '010001':
return 17;
case '010010':
return 18;
case '010011':
return 19;
case '010100':
return 20;
case '010101':
return 21;
case '010110':
return 22;
case '010111':
return 23;
case '011000':
return 24;
case '011001':
return 25;
case '011010':
return 26;
case '011011':
return 27;
case '011100':
return 28;
case '011101':
return 29;
case '011110':
return 30;
case '011111':
return 31;
case '100000':
return 32;
case '100001':
return 33;
case '100010':
return 34;
case '100011':
return 35;
case '100100':
return 36;
case '100101':
return 37;
case '100110':
return 38;
case '100111':
return 39;
case '101000':
return 40;
case '101001':
return 41;
case '101010':
return 42;
case '101011':
return 43;
case '101100':
return 44;
case '101101':
return 45;
case '101110':
return 46;
case '101111':
return 47;
case '110000':
return 48;
case '110001':
return 49;
case '110010':
return 50;
case '110011':
return 51;
case '110100':
return 52;
case '110101':
return 53;
case '110110':
return 54;
case '110111':
return 55;
case '111000':
return 56;
case '111001':
return 57;
case '111010':
return 58;
case '111011':
return 59;
case '111100':
return 60;
case '111101':
return 61;
case '111110':
return 62;
case '111111':
return 63;
}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/eslint/eslint.js
================================================
function findFit(code, { babylon, babelTraverse, babelGenerator, log }) {
const ast = babylon.parse(code);
babelTraverse(ast);
log(babelGenerator(ast).code);
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/eslint/eslint.test.js
================================================
function testThings(findDebugger, callback, args) {
const log = args.log;
const tests = [
{
title: `describe('test', ()=>{
fit('test', ()=>{
expect(true).toBe(true);
})
})`,
test(func, args) {
let result = func(this.title, args);
return result && result.includes && !result.includes('fit');
}
},
{
title: `describe('test', ()=>{
fit('fits?', ()=>{
let fit = true;
expect(fit).toBe(true);
})
})`,
test(func, args) {
let result = func(this.title, args);
return (
result &&
result.includes &&
!result.includes('fit(') &&
result.includes('let fit')
);
}
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
let pass = test.test(findDebugger, args);
results.push({ title: test.title, pass: pass });
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({ title: test.title, pass: false });
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/find-console-log-babel.solved.ts
================================================
// tslint:ignore
function findConsoleLogSolved(code, { babylon, babelTraverse, log }) {
return babylon
.parse(code)
.program.body.some(node => node.type === 'ConsoleLogStatement');
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/find-console-log-babel.ts
================================================
function findConsoleLogBabel(code, { babylon, babelTraverse, log }) {
// Find debugger!!
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/find-console-log-regex.solved.js
================================================
function findConsoleLog(code) {
return code
.replace(/\/\/.*|'.*?[^\\]'|".*?"|`[\s\S]*`|\/\*[\s\S]*\*\//)
.match(/\bconsole\s*.log\(/);
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/find-console-log.js
================================================
function findConsoleLog(code) {}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/find-console-log.test.js
================================================
function testThings(findConsoleLog, callback, args) {
const log = args.log;
const tests = [
{
code: `console.log('🐶🐶🐶')`,
expected: true
},
{
code: `lolconsole.log()`,
expected: false
},
{
code: `// don't use console.log();`,
expected: false
},
{
code: `'console.log()'`,
expected: false
},
{
code: `'hi'; console.log(123); 'bye'`,
expected: true
},
{
code: `console
.log()`,
expected: true
},
{
code: `'Fake //'; console.log(123); `,
expected: true
},
{
code: `"console.log(123)"`,
expected: false
},
{
code: `\`
console.log(123)\``,
expected: false
},
{
code: `' \\' console.log(123)'`,
expected: false
},
{
code: `console.lol()`,
expected: false
},
{
code: `"hello".log()`,
expected: false
},
{
code: `/* \`
*/ console.log(123); \`
\` + ''`,
expected: true
},
{
code: `function hello() {
console.log(123);
}`,
expected: true
},
{
code: `console.log({
hello: 123
})`,
expected: true
},
{
code: `console.log`,
expected: false
},
{
code: `hello(console.log)`,
expected: false
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
const result = !!findConsoleLog(test.code, args);
const pass = result === test.expected;
results.push({
code: test.code,
expected: test.expected,
result,
pass
});
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({ code: test.code, pass: false });
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/remove-console-log.solved.ts
================================================
function removeConsoleLogSolved(
code,
{ babelGenerator, babylon, babelTraverse, types }
) {
const ast = babylon.parse(code);
babelTraverse(ast, {
MemberExpression(path) {
if (
types.isIdentifier(path.node.object, { name: 'console' }) &&
types.isIdentifier(path.node.property, { name: 'log' }) &&
types.isCallExpression(path.parent) &&
path.parentKey === 'callee'
) {
path.parentPath.remove();
}
}
});
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/remove-console-log.test.js
================================================
function testThings(findConsoleLog, callback, args) {
const log = args.log;
const tests = [
{
code: `console.log('🐶🐶🐶')`,
expected: ''
},
{
code: `lolconsole.log();`,
expected: 'lolconsole.log();'
},
{
code: `console.lol();`,
expected: `console.lol();`
},
{
code: `// don't use console.log();`,
expected: `// don't use console.log();`
},
{
code: `'console.log()';`,
expected: `'console.log()';`
},
{
code: `'hi'; console.log(123); 'bye';`,
expected: `'hi';\n'bye';`
},
{
code: `console
.log()`,
expected: ``
},
{
code: `"console.log(123)";`,
expected: `"console.log(123)";`
},
{
code: `\`
console.log(1234)\`;`,
expected: `\`
console.log(1234)\`;`
},
{
code: `' \\' console.log(123)';`,
expected: `' \\' console.log(123)';`
},
{
code: `/* \`
*/ console.log(123); \`
\` + '';`,
expected: `\`
\` + ''; /* \`
*/`
},
{
code: `hello(console.log);`,
expected: `hello(console.log);`
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
let result = false;
let error;
try {
result = findConsoleLog(test.code, args);
} catch (e) {
error = e;
}
const pass = result === test.expected;
results.push({
code: test.code,
expected: test.expected,
result,
pass,
error
});
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({ code: test.code, pass: false });
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/remove-console-log.ts
================================================
function removeConsoleLog(
code,
{ babelGenerator, babylon, babelTraverse, types }
) {
const ast = babylon.parse(code);
let hasConsoleLog = false;
babelTraverse(ast, {
MemberExpression(path) {
if (
types.isIdentifier(path.node.object, { name: 'console' }) &&
types.isIdentifier(path.node.property, { name: 'log' }) &&
types.isCallExpression(path.parent) &&
path.parentKey === 'callee'
) {
hasConsoleLog = true;
}
}
});
return hasConsoleLog;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/traverse-console-log-babel.solved.ts
================================================
function traverseConsoleLogSolved(
code,
{ babylon, babelTraverse, types, log }
) {
const ast = babylon.parse(code);
let hasConsoleLog = false;
babelTraverse(ast, {
MemberExpression(path) {
if (
path.node.property.type === 'Identifier' &&
path.node.property.name === 'log' &&
path.node.object.type === 'Identifier' &&
path.node.object.name === 'console' &&
path.parent.type === 'CallExpression' &&
path.key === 'callee'
) {
hasConsoleLog = true;
}
}
});
return hasConsoleLog;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/traverse-console-log-babel.solved2.ts
================================================
function traverseConsoleLogSolved2(code, { babylon, babelTraverse, types }) {
const ast = babylon.parse(code);
let hasConsoleLog = false;
babelTraverse(ast, {
MemberExpression(path) {
if (
types.isIdentifier(path.node.object, { name: 'console' }) &&
types.isIdentifier(path.node.property, { name: 'log' }) &&
types.isCallExpression(path.parent) &&
path.parentKey === 'callee'
) {
hasConsoleLog = true;
}
}
});
return hasConsoleLog;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-console-log/traverse-console-log-babel.ts
================================================
function traverseConsoleLog(code, { babylon, babelTraverse, log }) {
const ast = babylon.parse(code);
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/find-debugger-babel.solved.ts
================================================
// tslint:ignore
function findDebuggerSolved(code, { babylon, babelTraverse, log }) {
return babylon
.parse(code)
.program.body.some(node => node.type === 'DebuggerStatement');
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/find-debugger-babel.ts
================================================
function findDebuggerBabel(code, { babylon, babelTraverse, log }) {
// Find debugger!!
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/find-debugger-regex.solved.js
================================================
function findDebugger(code) {
return code
.replace(/\/\/.*|'.*?[^\\]'|".*?"|`[\s\S]*`|\/\*[\s\S]*\*\//)
.match(/\bdebugger\b/);
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/find-debugger.js
================================================
function findDebugger(code) {}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/find-debugger.test.js
================================================
function testThings(findDebugger, callback, args) {
const log = args.log;
const tests = [
{
title: `debugger`,
shouldPass: true,
test(func, args) {
return !!func(this.title, args);
}
},
{
title: `debuggerStart()`,
shouldPass: false,
test(func, args) {
return !func(this.title, args);
}
},
{
title: `//debugger
console.log('hi');`,
shouldPass: false,
test(func, args) {
return !func(this.title, args);
}
},
{
title: `'debugger'`,
shouldPass: false,
test(func, args) {
return !func(this.title, args);
}
},
{
title: `'hi'; debugger; 'bye'`,
shouldPass: true,
test(func, args) {
return func(this.title, args);
}
},
{
title: `'Fake //'; debugger; `,
shouldPass: true,
test(func, args) {
return func(this.title, args);
}
},
{
title: `"debugger"`,
shouldPass: false,
test(func, args) {
return !func(this.title, args);
}
},
{
title: `\`
debugger\``,
shouldPass: false,
test(func, args) {
return !func(this.title, args);
}
},
{
title: `' \\' debugger'`,
test(func, args) {
return !func(this.title, args);
},
shouldPass: false
},
{
title: `/* \`
*/ debugger; \`
\` + ''`,
test(func, args) {
return func(this.title, args);
}
},
{
title: `function hello(){ debugger; }
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
`,
test(func, args) {
return func(this.title, args);
}
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
let pass = test.test(findDebugger, args);
results.push({
shouldPass: test.shouldPass,
title: test.title,
pass: pass
});
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({
shouldPass: test.shouldPass,
title: test.title,
pass: false
});
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/hint.js
================================================
debugger;
function hello() {
debugger;
}
// There
function hello3() {
return 'debugger in a string';
}
// There is no debugger
function hello2() {}
// there's no debugger
function hello3() {}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/remove-debugger.solved.ts
================================================
function removeDebuggerSolved(
code,
{ babylon, babelTraverse, babelGenerator, log }
) {
const ast = babylon.parse(code);
babelTraverse(ast, {
DebuggerStatement: node => {
node.remove();
}
});
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/remove-debugger.test.js
================================================
function testThings(findDebugger, callback, args) {
const log = args.log;
const tests = [
{
title: `debugger`,
test(func, args) {
return '' === func(this.title, args);
}
},
{
title: `debuggerStart();`,
test(func, args) {
return this.title === func(this.title, args);
}
},
{
title: `//debugger
console.log('hi');`,
test(func, args) {
return this.title === func(this.title, args);
}
},
{
title: `'debugger';`,
test(func, args) {
return this.title === func(this.title, args);
}
},
{
title: `'hi'; debugger; 'bye';`,
test(func, args) {
return (
`'hi';
'bye';` === func(this.title, args)
);
}
},
{
title: `'Fake //'; debugger; `,
test(func, args) {
return `'Fake //';` === func(this.title, args);
}
},
{
title: `"debugger";`,
test(func, args) {
return this.title === func(this.title, args);
}
},
{
title: `\`
debugger\`;`,
test(func, args) {
return this.title === func(this.title, args);
}
},
{
title: `' \\' debugger';`,
test(func, args) {
return this.title === func(this.title, args);
}
},
{
title: `/* \`
*/ debugger; \`
\` + '';`,
test(func, args) {
return !func(this.title, args).includes('debugger');
}
},
{
title: `function hello(){ debugger; }`,
test(func, args) {
return `function hello() {}` === func(this.title, args);
}
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
let pass = test.test(findDebugger, args);
results.push({ title: test.title, pass: pass });
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({ title: test.title, pass: false });
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/remove-debugger.ts
================================================
function removeDebugger(code, { babylon, babelTraverse, babelGenerator, log }) {
const ast = babylon.parse(code);
let hasDebugger = false;
babelTraverse(ast, {
DebuggerStatement: node => {
hasDebugger = true;
}
});
return hasDebugger;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/traverse-debugger-babel.solved.ts
================================================
function traverseDebuggerSolved(code, { babylon, babelTraverse, log }) {
const ast = babylon.parse(code);
let hasCode = false;
babelTraverse(ast, {
DebuggerStatement: () => {
hasCode = true;
}
});
return hasCode;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-debugger/traverse-debugger-babel.ts
================================================
function traverseDebugger(code, { babylon, babelTraverse, log }) {
const ast = babylon.parse(code);
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-fit/find-fit.js
================================================
function findFit(code, { babylon, babelTraverse, babelGenerator, log }) {
const ast = babylon.parse(code);
babelTraverse(ast);
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-fit/find-fit.solved.js
================================================
function findFit(code, { babylon, babelTraverse, babelGenerator, log }) {
const ast = babylon.parse(code);
babelTraverse(ast, {
Identifier: ({ node, parentPath }) => {
if (node.name === 'fit' && parentPath.isCallExpression()) {
node.name = 'it';
}
}
});
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/find-fit/find-fit.test.js
================================================
function testThings(findDebugger, callback, args) {
const log = args.log;
const tests = [
{
title: `describe('test', ()=>{
fit('test', ()=>{
expect(true).toBe(true);
})
})`,
test(func, args) {
let result = func(this.title, args);
return result && result.includes && !result.includes('fit');
}
},
{
title: `describe('test', ()=>{
fit('fits?', ()=>{
let fit = true;
expect(fit).toBe(true);
})
})`,
test(func, args) {
let result = func(this.title, args);
return (
result &&
result.includes &&
!result.includes('fit(') &&
result.includes('let fit')
);
}
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
let pass = test.test(findDebugger, args);
results.push({ title: test.title, pass: pass });
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({ title: test.title, pass: false });
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/hello-world.json
================================================
{
"type": "File",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"program": {
"type": "Program",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
},
"sourceType": "script",
"body": [
{
"type": "DebuggerStatement",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
}
}
],
"directives": []
},
"comments": [],
"tokens": [
{
"type": {
"label": "debugger",
"keyword": "debugger",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"value": "debugger",
"start": 0,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 0
},
"end": {
"line": 1,
"column": 8
}
}
},
{
"type": {
"label": "eof",
"beforeExpr": false,
"startsExpr": false,
"rightAssociative": false,
"isLoop": false,
"isAssign": false,
"prefix": false,
"postfix": false,
"binop": null,
"updateContext": null
},
"start": 8,
"end": 8,
"loc": {
"start": {
"line": 1,
"column": 8
},
"end": {
"line": 1,
"column": 8
}
}
}
]
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/it-lines/it-lines.js
================================================
function addItByLine(
code,
line,
{ babylon, babelTraverse, babelGenerator, log }
) {
const ast = babylon.parse(code);
babelTraverse(ast, {
Identifier: ({ node, parentPath }) => {
if (node.name === 'fit' && parentPath.isCallExpression()) {
node.name = 'it';
}
}
});
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/it-lines/it-lines.solved.js
================================================
function addItByLine(
code,
line,
{ babylon, babelTraverse, babelGenerator, log }
) {
const ast = babylon.parse(code);
babelTraverse(ast, {
Identifier: ({ node, parentPath }) => {
if (node.name === 'it' && parentPath.isCallExpression()) {
if (node.loc.start.line === line) {
node.name = 'fit';
}
}
}
});
return babelGenerator(ast).code;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/it-lines/it-lines.test.js
================================================
function testThings(findDebugger, callback, args) {
const log = args.log;
const tests = [
{
title: `// Line 3
describe('test', ()=>{
it('test', ()=>{
expect(true).toBe(true);
})
})`,
test(func, args) {
let result = func(this.title, 3, args);
return result && result.includes && result.includes('fit');
}
},
{
title: `// Line 22
describe('test', ()=>{
it('test', ()=>{
expect(true).toBe(true);
})
})`,
test(func, args) {
let result = func(this.title, 1, args);
return result && result.includes && !result.includes('fit');
}
}
];
let results = [];
let failed = false;
for (let i in tests) {
if (tests.hasOwnProperty(i)) {
let test = tests[i];
if (!failed) {
const logs = [];
args.log = value => {
logs.push(value);
};
let pass = test.test(findDebugger, args);
results.push({ title: test.title, pass: pass });
if (!pass) {
failed = true;
logs.map(log);
}
} else {
results.push({ title: test.title, pass: false });
}
}
}
callback(results);
}
/**
return code
.replace(/\/\/.*|'.*?[^\\]'|".*"|`[\s\S]+`/)
.match(/\bdebugger\b/)
*/
================================================
FILE: apps/kirjs/src/app/modules/ast/samples/tricky.js
================================================
debugger;
debuggerStart();
//debugger
console.log('hi');
console.log('debugger');
`'hi'; debugger; 'bye'`;
let x = 'Fake //';
('debugger');
`
debugger\`
' \\' debugger'
`;
/* \`
*/
debugger;
`
\` + ''`;
function hello() {
debugger;
}
console.log(
1,
function hello() {
console.log(function hi() {
console.log(123);
});
},
3,
4,
5,
6
);
call(1, 2, 3, 4, 5, 5);
describe('test', () => {
fit('test', () => {
expect(true).toBe(true);
});
});
describe('test', () => {
fit('fits?', () => {
let fit = true;
expect(fit).toBe(true);
});
});
================================================
FILE: apps/kirjs/src/app/modules/ast/size-picker/size-picker.component.css
================================================
.button:hover {
background: #444444;
color: white;
}
.button {
border: 1px solid #000;
border-radius: 50%;
height: 1vw;
width: 1vw;
cursor: pointer;
display: inline-block;
text-align: center;
line-height: 1vw;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/size-picker/size-picker.component.html
================================================
- {{ size }}
+
================================================
FILE: apps/kirjs/src/app/modules/ast/size-picker/size-picker.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SizePickerComponent } from './size-picker.component';
describe('SizePickerComponent', () => {
let component: SizePickerComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SizePickerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SizePickerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/ast/size-picker/size-picker.component.ts
================================================
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
@Component({
selector: 'kirjs-size-picker',
templateUrl: './size-picker.component.html',
styleUrls: ['./size-picker.component.css']
})
export class SizePickerComponent {
@Input() size = 28;
@Input() step = 2;
@Output() sizeChange = new EventEmitter();
}
================================================
FILE: apps/kirjs/src/app/modules/ast/size-picker/size-picker.module.ts
================================================
import { NgModule } from '@angular/core';
import { SizePickerComponent } from './size-picker.component';
@NgModule({
declarations: [SizePickerComponent],
exports: [SizePickerComponent]
})
export class SizePickerModule {}
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/babel-test-runner/babel-test-runner.component.css
================================================
:host {
height: 100%;
display: flex;
}
.runner {
display: none;
}
.test {
margin-bottom: 5px;
background: #ff9a86;
padding: 5px 10px;
font-size: 3vw;
}
.test.pass {
background: #b2ff37;
}
.mini {
font-size: 30%;
display: block;
}
pre {
margin: 0;
}
.test.future.future {
background: #e8e8e8;
color: #ddd;
}
.wrapper {
display: flex;
height: 100%;
}
.test-point:hover {
border: 3px black solid;
}
.test-point {
width: 32px;
height: 32px;
border-radius: 50%;
display: inline-block;
margin-left: 5px;
background: #999999;
}
.test-point.pass {
background: #b2ff37;
border: 1px solid #444444;
}
.test-point.current {
background: #ff9a86;
border: 1px solid #444444;
}
code.failing {
color: #ff4e3d;
}
code {
font-size: 3vw;
margin-top: 4px;
display: block;
margin-bottom: 24px;
}
mat-card {
width: 100%;
height: 100%;
}
.label {
color: #444;
font-size: 1.5vw;
}
.summary .label {
margin-bottom: 12px;
}
.summary {
padding-bottom: 24px;
}
.results {
padding-top: 24px;
}
.ast-full {
background: #ffffff;
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
padding: 20px;
}
kirjs-size-picker {
margin-left: 20px;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/babel-test-runner/babel-test-runner.component.html
================================================
Running {{ tests.length }} tests.
firstFailingIndex()"
(mouseenter)="displayedTest = test"
(mouseout)="displayedTest = null"
>
For the code:
{{ test.error }}
For the code:
{{test.code.trim()}}
Expected:
{{ test.expected | json }}
Actual:
{{ test.result | json }}
AST:
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/babel-test-runner/babel-test-runner.component.ts
================================================
import { Component, Input } from '@angular/core';
import * as babylon from 'babylon';
import * as types from 'babel-types';
import babelTraverse from '@babel/traverse';
import babelGenerator from '@babel/generator';
import { TestInfo } from '../../../../../../../codelab/src/app/shared/interfaces/test-info';
declare const require;
@Component({
selector: 'kirjs-babel-test-runner',
templateUrl: './babel-test-runner.component.html',
styleUrls: ['./babel-test-runner.component.css']
})
export class BabelTestRunnerComponent {
tests: Array = [];
logs = [];
@Input() files: any[];
@Input() showAst = false;
scale = 10;
showFull = false;
firstFailing: TestInfo;
displayedTest: TestInfo;
run(files: Array) {
this.logs = [];
const args = {
babylon,
babelTraverse,
babelGenerator,
types,
log: value => {
this.logs.push(value);
}
};
const callback = result => {
if (result) {
this.tests = result;
this.firstFailing = this.tests.find(test => !test.pass);
}
};
try {
// tslint:disable
const func = eval('(' + files[0].code + ')');
eval('(' + files[1].code + ')')(func, callback, args);
} catch (e) {
console.log(e);
}
}
firstFailingIndex() {
const firstFailing = this.tests.findIndex(i => !i.pass);
return firstFailing === -1 ? this.tests.length : firstFailing;
}
runTests() {
this.run(this.files);
}
ngOnInit() {
this.runTests();
}
}
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/test-set.component.css
================================================
.show-solution .icon {
visibility: hidden;
}
.show-solution:hover .icon {
visibility: visible;
}
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/test-set.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/test-set.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TestSetComponent } from './test-set.component';
describe('TestSetComponent', () => {
let component: TestSetComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TestSetComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestSetComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/ast/test-set/test-set.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'kirjs-test-set',
templateUrl: './test-set.component.html',
styleUrls: ['./test-set.component.css']
})
export class TestSetComponent {
@Input() files = [];
@Input() fontSize = 30;
@Input() showAst = false;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/angular-flags/angular-flags.component.css
================================================
.flags {
font-size: 2vw;
white-space: pre-wrap;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/angular-flags/angular-flags.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/angular-flags/angular-flags.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AngularFlagsComponent } from './angular-flags.component';
describe('AngularFlagsComponent', () => {
let component: AngularFlagsComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AngularFlagsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AngularFlagsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/angular-flags/angular-flags.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-angular-flags',
templateUrl: './angular-flags.component.html',
styleUrls: ['./angular-flags.component.css']
})
export class AngularFlagsComponent implements OnInit {
number = 124;
flags = [
{ checked: false, name: 'None', value: 0 },
{ checked: false, name: 'TypeElement', value: 1 },
{ checked: false, name: 'TypeText', value: 2 },
{ checked: false, name: 'ProjectedTemplate', value: 4 },
{ checked: false, name: 'CatRenderNode', value: 3 },
{ checked: false, name: 'TypeNgContent', value: 8 },
{ checked: false, name: 'TypePipe', value: 16 },
{ checked: false, name: 'TypePureArray', value: 32 },
{ checked: false, name: 'TypePureObject', value: 64 },
{ checked: false, name: 'TypePurePipe', value: 128 },
{ checked: false, name: 'CatPureExpression', value: 224 },
{ checked: false, name: 'TypeValueProvider', value: 256 },
{ checked: false, name: 'TypeClassProvider', value: 512 },
{ checked: false, name: 'TypeFactoryProvider', value: 1024 },
{ checked: false, name: 'TypeUseExistingProvider', value: 2048 },
{ checked: false, name: 'LazyProvider', value: 4096 },
{ checked: false, name: 'PrivateProvider', value: 8192 },
{ checked: false, name: 'TypeDirective', value: 16384 },
{ checked: false, name: 'Component', value: 32768 },
{ checked: false, name: 'CatProviderNoDirective', value: 3840 },
{ checked: false, name: 'CatProvider', value: 20224 },
{ checked: false, name: 'OnInit', value: 65536 },
{ checked: false, name: 'OnDestroy', value: 131072 },
{ checked: false, name: 'DoCheck', value: 262144 },
{ checked: false, name: 'OnChanges', value: 524288 },
{ checked: false, name: 'AfterContentInit', value: 1048576 },
{ checked: false, name: 'AfterContentChecked', value: 2097152 },
{ checked: false, name: 'AfterViewInit', value: 4194304 },
{ checked: false, name: 'AfterViewChecked', value: 8388608 },
{ checked: false, name: 'EmbeddedViews', value: 16777216 },
{ checked: false, name: 'ComponentView', value: 33554432 },
{ checked: false, name: 'TypeContentQuery', value: 67108864 },
{ checked: false, name: 'TypeViewQuery', value: 134217728 },
{ checked: false, name: 'StaticQuery', value: 268435456 },
{ checked: false, name: 'DynamicQuery', value: 536870912 },
{ checked: false, name: 'TypeModuleProvider', value: 1073741824 },
{ checked: false, name: 'CatQuery', value: 201326592 },
{ checked: false, name: 'Types', value: 201347067 }
];
checked: string[];
constructor() {
this.syncCheckBoxes();
}
syncCheckBoxes() {
this.flags.forEach(f => (f.checked = !!(this.number & f.value)));
}
ngOnInit() {}
handleClick(checked: boolean, value: number) {
this.number = this.number | value;
this.syncCheckBoxes();
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/ascii/ascii.component.css
================================================
.parent {
display: grid;
grid-template-columns: repeat(12, 1fr);
}
.item {
background: #fff;
border-right: 1px #444 dotted;
border-bottom: 1px #444 dotted;
padding: 0.5vw;
display: flex;
justify-content: space-between;
}
.key {
vertical-align: top;
font-size: 2vw;
color: #666;
}
.value {
font-size: 3.5vw;
}
select {
border: 1px #444 solid;
background: #ffffff;
font-size: 3vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/ascii/ascii.component.html
================================================
Display character table for:
{{ e.key }}
{{ item.key }}
{{ item.value }}
================================================
FILE: apps/kirjs/src/app/modules/binary/ascii/ascii.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AsciiComponent } from './ascii.component';
describe('AsciiComponent', () => {
let component: AsciiComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AsciiComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AsciiComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/ascii/ascii.component.ts
================================================
import {
Component,
Input,
OnChanges,
OnInit,
SimpleChanges
} from '@angular/core';
function encode(from: number, to: number, encoding: string) {
return new TextDecoder(encoding)
.decode(new Uint8Array(to - from).map((a, i) => i + from).buffer as any)
.split('')
.map((value, i) => ({
key: i + from,
value
}));
}
const layouts = {};
@Component({
selector: 'kirjs-ascii',
templateUrl: './ascii.component.html',
styleUrls: ['./ascii.component.css']
})
export class AsciiComponent implements OnChanges {
@Input() param: string;
encodings = [
{
key: 'ascii',
value: encode(33, 128, 'ascii')
},
{
key: 'ascii - Page 2',
value: encode(128, 255, 'ascii')
},
{
key: 'windows-1251',
value: encode(128, 255, 'windows-1251')
},
{
key: 'KOI8-R',
value: encode(128, 255, 'KOI8-R')
},
{
key: 'utf-8',
value: encode(1000, 1255, 'utf-16')
}
];
encoding = this.encodings[0];
constructor() {
// d = new TextDecoder('windows-125').decode(new Uint8Array(255).map((a,i)=>i).buffer)
}
ngOnChanges(changes: SimpleChanges): void {
if ('param' in changes) {
this.encoding = this.encodings.find(a => a.key === this.param);
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-flat/binary-flat.component.css
================================================
.wrapper {
word-break: break-all;
padding: 40px;
}
.binary {
letter-spacing: 2vw;
}
.item,
.detail-item {
font-size: 3vw;
}
.item-1 {
background: #eee;
}
.detail-item {
padding: 20px;
}
.detail {
width: 60%;
word-break: break-all;
}
.field-header {
font-size: 2vw;
}
.raw-value {
width: 40%;
font-size: 2vw;
word-break: break-all;
padding-right: 1vw;
margin-right: 1vw;
border-right: 1px #444 solid;
}
button {
display: block;
width: 100%;
background: #e51400;
color: #fff;
padding: 1vw;
font-size: 4vw;
}
h3 {
font-size: 2vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-flat/binary-flat.component.html
================================================
{{
item.rawValue
}}
i"
class="detail-item item-{{ i % 2 }}"
style="display: flex"
>
{{ item.rawValue }}
{{ item.value }}
Next
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-flat/binary-flat.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryFlatComponent } from './binary-flat.component';
describe('BinaryFlatComponent', () => {
let component: BinaryFlatComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryFlatComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryFlatComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-flat/binary-flat.component.ts
================================================
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges
} from '@angular/core';
import { BinaryParser } from '../parser/binary-parser';
import { StringBinaryReader } from '../parser/readers/string-reader';
import { BinaryParentComponent } from '../binary-view/binary-parent/binary-parent.component';
export function flatten(
structure: any[],
nesting = 0,
parent = null,
path = []
) {
return structure.reduce((result, item) => {
if (item.type === 'object' || item.type === 'array') {
item.data = false;
item.nesting = nesting;
item.className = 'tbd';
result = result
.concat(item)
.concat(
flatten(
item.value,
nesting + 1,
item,
item.name ? [...path, item.name] : [...path]
)
);
} else {
item.data = true;
item.nesting = nesting;
item.parent = parent || item;
item.root = item.parent.root || item.parent;
item.path = path;
item.className = path.map(p => 'parent-' + p).join(' ');
result.push(item);
}
return result;
}, []);
}
@Component({
selector: 'kirjs-binary-flat',
templateUrl: './binary-flat.component.html',
styleUrls: ['./binary-flat.component.css']
})
export class BinaryFlatComponent implements OnChanges {
@Input() parser: BinaryParser;
@Input() binary: string;
@Output() updateBinary = new EventEmitter();
detailIndex = 30;
structure: { start: number };
constructor(private readonly root: BinaryParentComponent) {}
update(event, item) {
this.root.update(item, event.target.textContent);
}
ngOnChanges(changes: SimpleChanges): void {
if ('binary' in changes) {
this.structure = flatten(
this.parser.readOrdered(new StringBinaryReader(this.binary)).value
);
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-gif/binary-gif.component.css
================================================
img {
image-rendering: pixelated !important;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-gif/binary-gif.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-gif/binary-gif.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryGifComponent } from './binary-gif.component';
describe('BinaryGifComponent', () => {
let component: BinaryGifComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryGifComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryGifComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-gif/binary-gif.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'kirjs-binary-gif',
templateUrl: './binary-gif.component.html',
styleUrls: ['./binary-gif.component.css']
})
export class BinaryGifComponent {
gif: string;
@Input()
set binary(binary: string) {
const binaries = binary.match(/.{8}/g);
const recombined = new Uint8Array(binaries.map(a => parseInt(a, 2)));
const b64encoded = btoa(
Array.from(recombined)
.map(a => String.fromCharCode(a))
.join('')
);
this.gif = 'data:image/gif;base64,' + b64encoded;
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-display/binary-display.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-display/binary-display.component.html
================================================
{{ bin }}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-display/binary-display.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryDisplayComponent } from './binary-display.component';
describe('BinaryDisplayComponent', () => {
let component: BinaryDisplayComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryDisplayComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryDisplayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-display/binary-display.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'kirjs-binary-display',
templateUrl: './binary-display.component.html',
styleUrls: ['./binary-display.component.css']
})
export class BinaryDisplayComponent {
binaries = [];
@Input() set binary(binary: string) {
this.binaries = binary.match(/.{8}/g);
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-inline.component.css
================================================
.wrapper {
display: grid;
grid-template-columns: 200px 100px 100%;
font-size: 20px;
}
.bin-block {
word-break: break-all;
}
[name='section-type'] {
font-size: 30px;
font-weight: 300;
}
.cell {
padding: 10px;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-inline.component.html
================================================
{{ item.displayValue || item.value }}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-inline.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryInlineComponent } from './binary-inline.component';
describe('BinaryInlineComponent', () => {
let component: BinaryInlineComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryInlineComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryInlineComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-inline.component.ts
================================================
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import { flatten } from '../binary-flat/binary-flat.component';
import { StringBinaryReader } from '../parser/readers/string-reader';
import { BinaryParser } from '../parser/binary-parser';
@Component({
selector: 'kirjs-binary-inline',
templateUrl: './binary-inline.component.html',
styleUrls: ['./binary-inline.component.css']
})
export class BinaryInlineComponent implements OnChanges {
@Input() filterClassName = /./;
@Input() parser: BinaryParser;
@Input() binary: string;
structure: any[];
ngOnChanges(changes: SimpleChanges): void {
if ('binary' in changes) {
try {
this.structure = flatten(
this.parser.readOrdered(new StringBinaryReader(this.binary)).value
).filter(a => a.className.match(this.filterClassName));
} catch (e) {
console.log(e);
}
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-inline/binary-inline.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BinaryInlineComponent } from './binary-inline.component';
import { BinaryDisplayComponent } from './binary-display/binary-display.component';
@NgModule({
declarations: [BinaryInlineComponent, BinaryDisplayComponent],
exports: [BinaryInlineComponent],
imports: [CommonModule]
})
export class BinaryInlineModule {}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-parser-demo/binary-parser-demo.component.css
================================================
.error::before {
color: #e51400;
content: '🙀';
}
.error {
color: #e51400;
border: 1px #e51400 solid;
border-radius: 12px;
font-size: 3vw;
padding: 2vw;
display: block;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-parser-demo/binary-parser-demo.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-parser-demo/binary-parser-demo.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryParserDemoComponent } from './binary-parser-demo.component';
describe('BinaryParserDemoComponent', () => {
let component: BinaryParserDemoComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryParserDemoComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryParserDemoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-parser-demo/binary-parser-demo.component.ts
================================================
import { Component, EventEmitter, Input, OnInit } from '@angular/core';
import { Parser } from 'binary-parser';
import { Buffer } from 'buffer';
import { bin2hex } from '../shared';
(window as any).Buffer = Buffer;
@Component({
selector: 'kirjs-binary-parser-demo',
templateUrl: './binary-parser-demo.component.html',
styleUrls: ['./binary-parser-demo.component.css']
})
export class BinaryParserDemoComponent implements OnInit {
@Input() helpers;
code = '';
@Input() filterClassName = /./;
result = '';
@Input() binary = '';
error = '';
onCodeChange = new EventEmitter();
constructor() {}
ngOnInit() {
this.code = this.helpers[0];
this.generateCode();
}
generateCode() {
this.onCodeChange.emit(this.code);
this.error = '';
try {
const parser = new Function(
'Parser',
'const parser = ' + this.code + '; return parser;'
)(Parser);
this.result = JSON.stringify(
parser.parse(Buffer.from(bin2hex(this.binary), 'hex')),
null,
' '
);
} catch (e) {
this.error = e.message;
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-plain/binary-plain.component.css
================================================
.wrapper {
display: flex;
flex-direction: column;
}
button {
font-size: inherit;
}
.legend {
width: 200px;
font-size: 2vw;
padding: 3vw 0;
display: flex;
}
.legend .item {
margin-right: 10px;
background: #dddddd;
border-radius: 4px;
padding: 0.5vw 1vw;
}
.enums .enums {
background: #ff3302;
color: black;
}
.string .string {
background: #ffaf00;
color: black;
}
.const .const {
background: #aaaaaa;
color: black;
}
.number .number {
background: #00ff51;
color: black;
}
.hex .color,
.hex .hex {
background: #00a4ff;
color: black;
}
.boolean .boolean {
background: #f600ff;
color: black;
}
.binary {
white-space: normal;
font-size: 3vw;
word-break: break-all;
padding-right: 3vw;
}
.spacing .binary {
display: flex;
flex-wrap: wrap;
}
.spacing .bin-block {
margin-right: 4vw;
white-space: nowrap;
}
.groups .parent-palette {
background: #00ff51;
}
.groups .parent-header {
background: #ffaf00;
}
.groups .parent-extensions {
background: #00a4ff;
}
.spacing {
line-height: 50px;
}
.mini .legend {
font-size: 1.2vw;
padding: 0;
}
.mini .legend .item {
padding: 0.2vw;
height: 2vw;
}
.mini input[type='checkbox'] {
zoom: 2 !important;
}
.mini .spacing {
line-height: 2vw;
}
.item-header:first-child {
margin-top: 0;
}
.item-header {
width: 100%;
font-size: 5vw;
display: block;
margin-bottom: 2vw;
margin-top: 4vw;
}
.selected {
width: 100%;
border: 1px #ddd solid;
padding: 1vw;
}
.label {
display: inline-block;
width: 24vw;
flex-shrink: 0;
}
.meta {
margin-top: 2vw;
margin-bottom: 1vw;
}
.selected .bin-block {
font-size: 4vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-plain/binary-plain.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-plain/binary-plain.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryPlainComponent } from './binary-plain.component';
describe('BinaryPlainComponent', () => {
let component: BinaryPlainComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryPlainComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryPlainComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-plain/binary-plain.component.ts
================================================
import {
Component,
EventEmitter,
Input,
OnChanges,
Output
} from '@angular/core';
import { BinaryParser } from '../parser/binary-parser';
import { StringBinaryReader } from '../parser/readers/string-reader';
import { flatten } from '../binary-flat/binary-flat.component';
@Component({
selector: 'kirjs-binary-plain',
templateUrl: './binary-plain.component.html',
styleUrls: ['./binary-plain.component.css']
})
export class BinaryPlainComponent implements OnChanges {
@Output() updateChunk = new EventEmitter();
@Input() parser: BinaryParser;
@Input() highlightGroups = false;
@Input() filterClassName = /./;
@Input() mini = false;
@Input() showPopups = false;
hackHack = {
0x01: 'Types',
0x02: 'Import',
0x03: 'Function',
0x05: 'Table',
0x07: 'Export',
0x08: 'Start',
0x0a: 'Code'
};
show = [];
types = ['boolean', 'number', 'hex', 'string', 'const', 'enums'];
@Input()
spacing = false;
@Input()
highlightedMap = this.types.reduce((r, v) => {
r[v] = false;
return r;
}, {});
structure: any[];
@Input() binary: string;
get highlighted() {
return Object.keys(this.highlightedMap)
.filter(key => this.highlightedMap[key])
.join(' ');
}
ngOnChanges() {
if (this.binary && this.parser) {
try {
this.structure = flatten(
this.parser.readOrdered(new StringBinaryReader(this.binary)).value
).filter(a => a.className.match(this.filterClassName));
} catch (e) {
console.log(e);
}
}
}
update(chunk: any, value: any) {
this.updateChunk.emit({ chunk, value });
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/array/array.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/array/array.component.html
================================================
{{ item.name }}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/array/array.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ArrayComponent } from './array.component';
describe('ArrayComponent', () => {
let component: ArrayComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ArrayComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ArrayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/array/array.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-array',
templateUrl: './array.component.html',
styleUrls: ['./array.component.css']
})
export class ArrayComponent implements OnInit {
@Input() data;
@Input() showMeta: boolean;
constructor() {}
trackBy(i, data) {
return data.index;
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/binary-parent/binary-parent.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/binary-parent/binary-parent.component.scss
================================================
:host ::ng-deep {
.block {
display: block;
font-size: 30px;
margin-left: 40px;
}
.name {
width: 200px;
}
kirjs-object .block > .name {
display: block;
}
}
.block {
margin-left: 0;
font-size: 40px;
}
:host ::ng-deep {
input {
background: transparent;
font-size: inherit;
border: 0 solid;
font-family: Monaco, 'Lucida Console', monospace;
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/binary-parent/binary-parent.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BinaryParentComponent } from './binary-parent.component';
describe('BinaryParentComponent', () => {
let component: BinaryParentComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BinaryParentComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BinaryParentComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/binary-parent/binary-parent.component.ts
================================================
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output
} from '@angular/core';
import { BinaryParser } from '../../parser/binary-parser';
import { StringBinaryReader } from '../../parser/readers/string-reader';
@Component({
selector: 'kirjs-binary-parent',
templateUrl: './binary-parent.component.html',
styleUrls: ['./binary-parent.component.scss']
})
export class BinaryParentComponent implements OnInit, OnChanges {
@Input() showMeta = true;
@Input() parser: BinaryParser;
@Input() binary: string;
@Input() type = 'structure';
@Input() spacing = false;
@Output() updateBinary = new EventEmitter();
structure: any;
constructor() {}
ngOnInit() {
this.regenerate();
}
ngOnChanges() {
this.regenerate();
}
regenerate() {
this.structure = this.parser.readOrdered(
new StringBinaryReader(this.binary)
);
}
update(chunk, value) {
const len = chunk.end - chunk.start;
value = value.padEnd(len, 0).slice(0, len);
this.binary =
this.binary.slice(0, chunk.start) + value + this.binary.substr(chunk.end);
this.updateBinary.emit(this.binary);
this.regenerate();
}
updatePart(chunk, value) {
const len = chunk.end - chunk.start;
value = value.padEnd(len, 0).slice(0, len);
this.binary =
this.binary.slice(0, chunk.start) + value + this.binary.substr(chunk.end);
//
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/binary-view.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { BlockComponent } from './block/block.component';
import { ObjectComponent } from './object/object.component';
import { BitsComponent } from './bits/bits.component';
import { StringComponent } from './string/string.component';
import { NumberComponent } from './number/number.component';
import { ArrayComponent } from './array/array.component';
import { ColorComponent } from './color/color.component';
import { BinaryParentComponent } from './binary-parent/binary-parent.component';
import { HexComponent } from './hex/hex.component';
import { InlineComponent } from './inline/inline.component';
import { InlineRootComponent } from './inline-root/inline-root.component';
import { BinaryFlatComponent } from '../binary-flat/binary-flat.component';
import { BinaryPlainComponent } from '../binary-plain/binary-plain.component';
import { FormsModule } from '@angular/forms';
@NgModule({
imports: [CommonModule, FormsModule],
declarations: [
BlockComponent,
ObjectComponent,
BitsComponent,
StringComponent,
NumberComponent,
ArrayComponent,
ColorComponent,
BinaryParentComponent,
BinaryFlatComponent,
HexComponent,
InlineComponent,
InlineRootComponent,
BinaryPlainComponent
],
exports: [
BinaryFlatComponent,
BinaryPlainComponent,
BlockComponent,
ObjectComponent,
BitsComponent,
StringComponent,
NumberComponent,
ArrayComponent,
ColorComponent,
BinaryParentComponent,
HexComponent
],
entryComponents: [
BinaryFlatComponent,
BlockComponent,
ObjectComponent,
BitsComponent,
StringComponent,
NumberComponent,
ArrayComponent,
ColorComponent,
HexComponent,
BinaryPlainComponent
]
})
export class BinaryViewModule {}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/bits/bits.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/bits/bits.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/bits/bits.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BitsComponent } from './bits.component';
describe('BitsComponent', () => {
let component: BitsComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BitsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BitsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/bits/bits.component.ts
================================================
import { Component, Input } from '@angular/core';
import { BinaryParentComponent } from '../binary-parent/binary-parent.component';
@Component({
selector: 'kirjs-bits',
templateUrl: './bits.component.html',
styleUrls: ['./bits.component.css']
})
export class BitsComponent {
@Input() data;
@Input() showMeta = false;
constructor(private readonly root: BinaryParentComponent) {}
update(value) {
this.root.update(this.data, Number(value).toString());
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/block/block.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/block/block.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/block/block.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BlockComponent } from './block.component';
describe('BlockComponent', () => {
let component: BlockComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BlockComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BlockComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/block/block.component.ts
================================================
import {
ChangeDetectorRef,
Component,
ComponentFactoryResolver,
Input,
OnChanges,
OnInit,
ViewContainerRef
} from '@angular/core';
import { ObjectComponent } from '../object/object.component';
import { BitsComponent } from '../bits/bits.component';
import { StringComponent } from '../string/string.component';
import { NumberComponent } from '../number/number.component';
import { ArrayComponent } from '../array/array.component';
import { ColorComponent } from '../color/color.component';
import { HexComponent } from '../hex/hex.component';
const componentMap = {
object: ObjectComponent,
bits: BitsComponent,
string: StringComponent,
number: NumberComponent,
array: ArrayComponent,
color: ColorComponent,
hex: HexComponent
};
@Component({
selector: 'kirjs-block',
templateUrl: './block.component.html',
styleUrls: ['./block.component.css']
})
export class BlockComponent implements OnInit, OnChanges {
@Input() data: any;
@Input() showMeta: boolean;
private componentRef: any;
constructor(
private vcr: ViewContainerRef,
private readonly cdr: ChangeDetectorRef,
private componentFactoryResolver: ComponentFactoryResolver
) {}
ngOnChanges(changes) {
if (this.componentRef && changes.showMeta) {
this.componentRef.instance.showMeta = changes.showMeta.currentValue;
}
if (this.componentRef && changes.data) {
this.componentRef.instance.data = changes.data.currentValue;
}
this.cdr.detectChanges();
}
ngOnInit() {
if (!componentMap[this.data.type]) {
// tslint:disable-next-line:no-debugger
debugger;
}
const cf = this.componentFactoryResolver.resolveComponentFactory(
componentMap[this.data.type]
);
this.vcr.clear();
this.componentRef = this.vcr.createComponent(cf) as any;
this.componentRef.instance.data = this.data;
this.componentRef.instance.showMeta = this.showMeta;
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/color/color.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/color/color.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/color/color.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ColorComponent } from './color.component';
describe('ColorComponent', () => {
let component: ColorComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ColorComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ColorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/color/color.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { BinaryParentComponent } from '../binary-parent/binary-parent.component';
@Component({
selector: 'kirjs-color',
templateUrl: './color.component.html',
styleUrls: ['./color.component.css']
})
export class ColorComponent implements OnInit {
color: string;
constructor(private readonly root: BinaryParentComponent) {}
private _data: any;
get data() {
return this._data;
}
@Input()
set data(data) {
this._data = data;
this.color = data.value.toString(16).padStart(6, 0);
}
update(value) {
const val = (parseInt(value.slice(1), 16).toString(2) as any).padStart(
24,
0
);
this.root.update(this._data, val);
}
updateBinary(binary) {
this.root.update(this.data, binary);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/hex/hex.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/hex/hex.component.html
================================================
{{ data.rawValue }}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/hex/hex.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HexComponent } from './hex.component';
describe('HexComponent', () => {
let component: HexComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [HexComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HexComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/hex/hex.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-hex',
templateUrl: './hex.component.html',
styleUrls: ['./hex.component.css']
})
export class HexComponent implements OnInit {
@Input() data;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline/inline.component.css
================================================
span {
font-size: 50px;
word-wrap: break-word;
}
.header {
background: #52ff93;
}
.palette {
background: #ffb71a;
}
.extensions {
background: #61e9ff;
}
.item:hover {
box-shadow: 0px 0px 4px 4px #444;
cursor: pointer;
}
input {
background: transparent;
border: 0 solid;
font-size: inherit;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline/inline.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline/inline.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InlineComponent } from './inline.component';
describe('InlineComponent', () => {
let component: InlineComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [InlineComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InlineComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline/inline.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { InlineRootComponent } from '../inline-root/inline-root.component';
import { BinaryParentComponent } from '../binary-parent/binary-parent.component';
@Component({
selector: 'kirjs-inline',
templateUrl: './inline.component.html',
styleUrls: ['./inline.component.css']
})
export class InlineComponent implements OnInit {
isArray: boolean;
private value: any;
constructor(
private readonly root: InlineRootComponent,
private readonly binaryRoot: BinaryParentComponent
) {}
private _data: any;
get data() {
return this._data;
}
@Input()
set data(data) {
this._data = data;
this.value = data.value;
this.isArray = Array.isArray(data.value);
}
updateValue(value) {
this.binaryRoot.update(this.data, value);
}
ngOnInit() {}
display() {
this.root.display(this.data);
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline-root/inline-root.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline-root/inline-root.component.html
================================================
{{ displayData | json }}
{{ displayData.name }}
{{ displayData.value }}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline-root/inline-root.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InlineRootComponent } from './inline-root.component';
describe('InlineRootComponent', () => {
let component: InlineRootComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [InlineRootComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InlineRootComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/inline-root/inline-root.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-inline-root',
templateUrl: './inline-root.component.html',
styleUrls: ['./inline-root.component.css']
})
export class InlineRootComponent implements OnInit {
@Input() data: any;
displayData: any;
constructor() {}
ngOnInit() {}
display(displayData: any) {
this.displayData = displayData;
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/number/number.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/number/number.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/number/number.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NumberComponent } from './number.component';
describe('NumberComponent', () => {
let component: NumberComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [NumberComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(NumberComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/number/number.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { BinaryParentComponent } from '../binary-parent/binary-parent.component';
@Component({
selector: 'kirjs-number',
templateUrl: './number.component.html',
styleUrls: ['./number.component.css']
})
export class NumberComponent implements OnInit {
@Input() data: any;
@Input() showMeta = false;
constructor(private readonly root: BinaryParentComponent) {}
update(value) {
const val = (Number(value).toString(2) as any).padStart(
this.data.length,
0
);
this.root.update(this.data, val.slice(8) + val.slice(0, 8));
}
updateBinary(binary) {
this.root.update(this.data, binary);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/object/object.component.css
================================================
.show-meta {
padding: 4px;
margin-right: 4px;
margin-top: 4px;
border: 1px #ddd solid;
border-radius: 10px;
font-size: 2vw;
}
.name {
display: none;
}
.show-meta .name {
display: inline-block;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/object/object.component.html
================================================
{{ item.name }}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/object/object.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ObjectComponent } from './object.component';
describe('ObjectComponent', () => {
let component: ObjectComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ObjectComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ObjectComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/object/object.component.ts
================================================
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-object',
templateUrl: './object.component.html',
styleUrls: ['./object.component.css']
})
export class ObjectComponent implements OnInit {
@Input() data: any;
@Input() showMeta = true;
constructor(private cdr: ChangeDetectorRef) {}
ngOnInit() {}
trackBy(i, data) {
return data.name;
}
init() {
this.cdr.detectChanges();
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/string/string.component.css
================================================
:host {
word-break: break-all;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/string/string.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/string/string.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StringComponent } from './string.component';
describe('StringComponent', () => {
let component: StringComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StringComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StringComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/binary-view/string/string.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { BinaryParentComponent } from '../binary-parent/binary-parent.component';
@Component({
selector: 'kirjs-string',
templateUrl: './string.component.html',
styleUrls: ['./string.component.css']
})
export class StringComponent implements OnInit {
get data() {
return this._data;
}
@Input()
set data(data) {
this._data = data;
}
@Input() showMeta = false;
_data: any;
constructor(private readonly root: BinaryParentComponent) {}
updateBinary(binary) {
this.root.update(this.data, binary);
}
update(value) {
const val = value
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.root.update(this.data, val);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary.component.html
================================================
@kirjs
️JavaScript ❤️ Binary
kirjs.com/binary/0
If you can't read the numbers below, move closer or go to
kirjs.com/binary/0
{{ binaryLittleGif.substr(0, 200) }}
Binary is hidden behind many layers of abstraction
In this talk:
Binary in files (images, video, other media, zip)
Binary for data transfer (instead of JSON)
Javascript using binary in memory
Let's see what's inside of gif
{{ chikinGif }}
{{ littleGif }}
{{ binaryLittleGif }}
Binary data makes no sense without a schema
Binary data
01001110 01101111 01110110 01101001 00100000 01110011 01100001
01100100
+
Schema
Lol, what's this?
=
Numbers? Picture? Archive? Text?
Binary data makes no sense without a schema
Binary data
01001110 01101111 01110110 01101001 00100000 01110011 01100001
01100100
+
Schema
This is UTF-8 string!
=
Novi Sad
GRAPHICS INTERCHANGE FORMAT(sm)
Version 89a
(c)1987,1988,1989,1990
Copyright
CompuServe Incorporated
Columbus, Ohio
Header
Palette (Optional, size defined )
Extensions (Optional)
Actual image data
Image Control
Animation Control
Comments
Convert binary to decimal with JavaScript
Convert decimal to binary
Convert binary to hexadecimal with JavaScript
Convert hexadecimal to binary with JavaScript
ПЕЛЬМЕНЬ
ПЕЛЬМЕНЬ
ПЕЉМЕЊ
┌─┬┐ ╔═╦╗ ╓─╥╖ ╒═╤╕
│ ││ ║ ║║ ║ ║║ │ ││
├─┼┤ ╠═╬╣ ╟─╫╢ ╞═╪╡
└─┴┘ ╚═╩╝ ╙─╨╜ ╘═╧╛
┌───────────────────┐
│ ╔═══╗ │
│ ╚═╦═╝ │
╞═╤══╩══╤═══════════╡
│ ├──┬──┤ │
│ └──┴──┘ │
└───────────────────┘
Get charcode from string
Get letter from charcode
Parsing binary(with binary-parser )
Gif facts
Image size: 1х1 to 65535х65535
Colors: 2 - 256
True color gifs are possible
Max number of animation frames - unlimited
Animation delay 1/100 - 655 seconds
There's a plain text extension
24 pages + 12 pages appendix in gif89 standard
File header constants
Gif - GIF87a (or GIF89a)
Jpeg - begin with ‘FF D8‘ and end with ‘FF D9'
Java class - CAFEBABE
ZIP files begin with ‘PK‘ (50 4B)
PDF files start with ‘%PDF‘ (25 50 44 46)
PNG image files begin with “\211 P N G \r \n 32 \n” (89 50 4E 47 0D 0A
1A 0A)
HTTP2 - PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n
Big library of binary formats
https://formats.kaitai.io/
Can we use binary instead of JSON ?
Debugging
JSON
Easy to understand
Human readable
Schema
Binary
Needs a schema
Comes with type checking
Comes with validation
Thought experiment
Memory management in JavaScript
Let's see how memory works in typed arrays
Angular Component Flags (bitmask)
ReactDOM Flags (bitmask)
The end:
Binary in files (images, video, other media, zip)
Binary for data transfer (instead of JSON)
Javascript using binary in memory
@kirjs
Binary ❤️ JavaScript
kirjs.com/binary/0
Thanks @andrey_sitnik, @_bmsdave, @NisamProgramer for review
================================================
FILE: apps/kirjs/src/app/modules/binary/binary.component.scss
================================================
h1 {
font-size: 5vw;
}
::ng-deep feedback-widget {
position: absolute;
right: 0;
bottom: 140px;
display: block;
width: 32px;
height: 32px;
z-index: 100;
.feedback-button {
width: 24px;
height: 24px;
}
}
#font-size b {
font-size: 10vw;
margin-bottom: 6vw;
display: block;
}
:host {
.binary-presentation ::ng-deep {
.slide [intro] {
background: url(pics/kirjs.webp);
background-size: 100vh !important;
padding-left: 3vw;
h2 {
font-size: 3vw;
}
h1,
h2,
h3 {
text-align: left;
margin-left: 100vh;
margin-bottom: 2vw;
}
h3 {
font-size: 3vw;
}
}
.slide [json] {
background: url(pics/p2.jpg);
}
.slide [krakoziabry] {
background: url(pics/krakoziabry.jpg);
background-size: cover;
}
.slide [endianness] {
background: url(pics/endianness.png);
background-size: 80vw !important;
}
.slide [pelmen] {
background: url(pics/pelmeni.jpg);
background-size: cover;
h1 {
font-size: 10vw;
text-shadow: 0 0 1vw #ffffff;
}
}
.slide [kaitai] {
background: url(pics/kaitai.png);
background-size: 400px 400px !important;
background-color: #fff;
h1 {
font-size: 3vw;
}
h2 {
text-align: center;
font-size: 2vw;
}
}
.slide [gif] {
background: url(pics/zoom.gif);
}
.slide #binary-abstraction {
background: url(pics/binary-abstraction.jpg);
}
.slide [message],
[gif] .slide {
background: url(pics/gdg-binary.jpg);
}
.slide [gif-bg],
[gif-bg] .slide {
background: url(pics/little.gif);
}
.slide [bg],
[bg] .slide {
background-repeat: no-repeat;
background-size: cover;
}
.slide [vs] {
display: flex;
align-items: center;
justify-content: center;
}
.slide h1 {
font-size: 5vw;
color: #444;
text-align: center;
}
h2 {
font-size: 4vw;
margin-bottom: 2vw;
}
[bg] h1 {
margin-top: 40px;
font-size: 10vw;
text-shadow: 0 3px 20px #fff, 0 -3px 20px #fff, -3px 0 20px #fff,
3px 0 20px #fff;
}
[bg] h2 {
font-size: 6vw;
text-shadow: 0 3px 20px #fff, 0 -3px 20px #fff, -3px 0 20px #fff,
3px 0 20px #fff;
}
td {
font-size: 2vw;
font-weight: 300;
padding: 1vw;
}
}
[bg] h1 b,
[bg] h2 b {
background: transparent;
font-weight: bold;
}
}
.compare-json {
width: 50%;
}
.compare-serialization {
padding: 0 20vw;
}
#gif-structure {
.text,
li {
font-size: 3vw;
}
h2 {
margin-bottom: 1vw;
}
.text {
margin-bottom: 2vw;
}
}
[binary-schema] {
table {
width: 100%;
}
td {
font-size: 4vw;
font-weight: 400;
}
}
#gif-structure {
.parent-palette {
background: #00ff51;
}
.parent-header {
background: #ffaf00;
}
.parent-extensions {
background: #00a4ff;
}
}
#bitwise-to-read .book {
background-image: url(./pics/hackers-delight.jpeg);
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary.component.ts
================================================
import { Component } from '@angular/core';
import { strToBin } from './parser/utils';
declare const require;
const littleGif = require('!binary-loader!./pics/little.gif');
const chikinGif = require('!binary-loader!./pics/chikin.gif');
@Component({
selector: 'kirjs-binary',
templateUrl: './binary.component.html',
styleUrls: ['./binary.component.scss']
})
export class BinaryComponent {
fontSize = 30;
binaryLittleGif = strToBin(littleGif);
binaryChikinGif = strToBin(chikinGif);
code = {
message: `['01001011', '01001111',
'01010100', '01001100',
'01001001', '01001110',
'00100000', '00111110',
'00100000', '01001010',
'01000001', '01010110',
'01000001'
]
// .map(
// a => String.fromCharCode(
// parseInt(a, 2)))
// .join('');
`,
simpleBinaryOperations: `let i = 7; // 7 = 1 + 2 + 4
!!(i & 1) // true
!!(i & 2) // true
!!(i & 4) // true
i = i & ~2; // 5 = 1 + 4 (Not 2)
!!(i & 1) // true
!!(i & 2) // false
!!(i & 4) // true
i = i | 2 // 7 = 1 + 2 + 4
!!(i & 1) // true
!!(i & 2) // true
!!(i & 4) // true
`,
jsonBasic: `{
"name": "Sarah",
"test": true,
"something": 1212
}`,
jsonOne: `{
"str": "1",
"number": 1,
"bool": true
}`,
inputFile: ' ',
fileHandlerHighlight: { match: /read/, className: 'highlighted-code' },
fileHandler: `const input = document.getElementById('file');
input.addEventListener('change', (e) => {
const reader = new FileReader();
reader.onloadend = (e) => {
console.log(e.target.result);
};
reader.readAsString(input.files[0]);
});
`,
fileHandlerBinary: `const input = document.getElementById('file');
input.addEventListener('change', (e) => {
const reader = new FileReader();
reader.onloadend = (e) => {
console.log(e.target.result);
};
reader.readAsArrayBuffer(input.files[0]);
});`
};
littleGif = littleGif;
chikinGif = chikinGif;
binaryParserHeaderMatch = /parent-header/;
binaryParserHeaderHelpers = [
`new Parser();`,
`new Parser()
.string('gif', {length: 3})
`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16('width')
`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
.bit1('globalPalette')
`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
.bit1('globalPalette')
.bit3('resolution')
.bit1('paletteSorted')
.bit3('paletteSize')
.uint8('background')
.uint8('ratio')
`
];
reactBitmask = `// Don't change these two values. They're used by React Dev Tools.
var NoEffect = /* */0;
var PerformedWork = /* */1;
// You can change the rest (and add more).
var Placement = /* */2;
var Update = /* */4;
var PlacementAndUpdate = /* */6;
var Deletion = /* */8;
var ContentReset = /* */16;
var Callback = /* */32;
var DidCapture = /* */64;
var Ref = /* */128;
var Snapshot = /* */256;
var Passive = /* */512;
// Passive & Update & Callback & Ref & Snapshot
var LifecycleEffectMask = /* */932;
// Union of all host effects
var HostEffectMask = /* */1023;
var Incomplete = /* */1024;
var ShouldCapture = /* */2048;`;
binaryParserPaletteMatch = /parent-palette/;
binaryParserPaletteHelpers = [
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
.bit1('globalPalette')
.bit3('resolution')
.bit1('paletteSorted')
.bit3('paletteSize')
.uint8('background')
.uint8('ratio')`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
.bit1('globalPalette')
.bit3('resolution')
.bit1('paletteSorted')
.bit3('paletteSize')
.uint8('background')
.uint8('ratio')
.array('palette', {
type: new Parser().bit24('color'),
length: 4
}
)`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
.bit1('globalPalette')
.bit3('resolution')
.bit1('paletteSorted')
.bit3('paletteSize')
.uint8('background')
.uint8('ratio')
.array('palette', {
type: new Parser().bit24('color'),
length: (result) =>
2 ** (result.paletteSize + 1)
}
)`,
`new Parser()
.string('gif', {length: 3})
.string('version', {length: 3})
.uint16le('width')
.uint16le('height')
.bit1('globalPalette')
.bit3('resolution')
.bit1('paletteSorted')
.bit3('paletteSize')
.uint8('background')
.uint8('ratio')
.array('palette', {
type: new Parser()
.uint8('r')
.uint8('g')
.uint8('b'),
length: (result) =>
2 ** (result.paletteSize + 1)
}
)`
];
//
// `file.byteLength`,
// `String.fromCharCode(...new Uint8Array(file))`,
// // `// Let's test how many arguments we can apply
// // String.fromCharCode(...Array.from(new Array(100)))`,
// // `String.fromCharCode(...Array.from(new Array(10000)))`,
// // `String.fromCharCode(...Array.from(new Array(100000)))`,
// // `String.fromCharCode(...Array.from(new Array(1000000)))`,
// // `String.fromCharCode(...Array.from(new Array(125307)))`,
// // `
// // // read more:
// https://stackoverflow.com/questions/22747068/is-there-a-max-number-of-arguments-javascript-functions-can-accept
// // String.fromCharCode(...Array.from(new Array(125306)))`,
// `String.fromCharCode(...new Uint8Array(file))`,
// `Array.from(new Uint8Array(file)).map(a=>a.toString(2).padStart(8, 0)).join('')`,
//
// document.getElementById('file').addEventListener('change', (e)=>{
// const reader = new FileReader();
//
// reader.onloadend = (e) => {
// file = e.target.result;
// console.log(file);
// };
//
// reader.readAsArrayBuffer(e.target.files[0]);
// })
// gif = {
// width: "4",
// height: "4",
// image: [
// '#f00', '#f00', '#f00', '#f00',
// '#f90', '#f0f', '#f00', '#f00',
// '#f90', '#f0f', '#f00', '#f00',
// '#f90', '#f0f', '#f00', '#f00',
// ]
// }
commands = [
`\`
@kirjs
Binary ❤️ JavaScript
\``,
` `,
`
document.getElementById('file').addEventListener('change', (e)=>{
const reader = new FileReader();
reader.onloadend = (e) => {
file = e.target.result;
console.log(file);
};
reader.readAsArrayBuffer(e.target.files[0]);
})`,
`file.byteLength`,
`String.fromCharCode(...new Uint8Array(file))`,
// `// Let's test how many arguments we can apply
// String.fromCharCode(...Array.from(new Array(100)))`,
// `String.fromCharCode(...Array.from(new Array(10000)))`,
// `String.fromCharCode(...Array.from(new Array(100000)))`,
// `String.fromCharCode(...Array.from(new Array(1000000)))`,
// `String.fromCharCode(...Array.from(new Array(125307)))`,
// `
// // read more: https://stackoverflow.com/questions/22747068/is-there-a-max-number-of-arguments-javascript-functions-can-accept
// String.fromCharCode(...Array.from(new Array(125306)))`,
`String.fromCharCode(...new Uint8Array(file))`,
`Array.from(new Uint8Array(file)).map(a=>a.toString(2).padStart(8, 0)).join('')`,
`explain('message', 'basic')`,
`explain('message', 'bytes')`,
`explain('bindec', 'uint8')`,
`parseInt('01010101', 2)`,
`explain('bindec', 'int8')`,
`explain('message', 'uint8')`,
`explain('ascii')`,
`explain('message', 'string')`,
`explain('compare')`,
`explain('json')`,
`explain('gif')`,
`
// Let's reinvent gif with JSON:
gif = {
width: "4",
height: "4",
image: [
'#f00', '#f00', '#f00', '#f00',
'#f90', '#f0f', '#f00', '#f00',
'#f90', '#f0f', '#f00', '#f00',
'#f90', '#f0f', '#f00', '#f00',
]
}
`,
`
// Let's index the colors
gif = {
width: "4",
height: "4",
colors: ['#f00', '#f90', '#f0f'],
image: [
0, 0, 0, 0,
1, 2, 0, 0,
1, 2, 0, 0,
1, 0 ,2 , 0
]
}
`,
`JSON.stringify(gif)`,
`JSON.stringify(gif).length`,
`"010010010111010000100000011010010111001100100000011000110110111101101101011011010110111101101110001"
`,
`explain('gif')`,
`// How to read binary data?`
];
private evaledMessage: string;
evalMessage() {
this.evaledMessage = eval(this.code.message);
}
setLittleGifBinary(value: string) {
this.littleGif = value;
this.binaryLittleGif = strToBin(value);
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/binary.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { MatSelectModule } from '@angular/material/select';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { ConsoleModule } from '@codelab/console';
import { CodeDemoModule } from '@codelab/code-demos';
import { SharedPipeModule } from '@codelab/utils/src/lib/pipes/pipes.module';
import { BinaryComponent } from './binary.component';
import { FakeGifComponent } from './fake-gif/fake-gif.component';
import { GifPaletteComponent } from './gif-palette/gif-palette.component';
import { BinaryViewModule } from './binary-view/binary-view.module';
import { MidiComponent } from './midi/midi.component';
import { AsciiComponent } from './ascii/ascii.component';
import { BindecComponent } from './bindec/bindec.component';
import { MessageComponent } from './message/message.component';
import { JsonComponent } from './json/json.component';
import { CompareComponent } from './compare/compare.component';
import { HtmlPostComponent } from './html-post/html-post.component';
import { NewProgressBarModule } from '../ast/new-progress-bar/new-progress-bar.module';
import { BinaryGifComponent } from './binary-gif/binary-gif.component';
import { BitComponent } from './bit/bit.component';
import { MemoryComponent } from './memory/memory.component';
import { BinaryParserDemoComponent } from './binary-parser-demo/binary-parser-demo.component';
import { HexdecComponent } from './hexdec/hexdec.component';
import { AngularFlagsComponent } from './angular-flags/angular-flags.component';
import { ColorIndexingComponent } from './color-indexing/color-indexing.component';
import { BitwiseComponent } from './bitwise/bitwise.component';
import { ToReadComponent } from './to-read/to-read.component';
const routes = RouterModule.forChild(SlidesRoutes.get(BinaryComponent));
@NgModule({
imports: [
routes,
FormsModule,
CommonModule,
BinaryViewModule,
CodeDemoModule,
MatAutocompleteModule,
SharedPipeModule,
ConsoleModule,
NewProgressBarModule,
MatSelectModule,
SlidesModule,
FeedbackModule
],
declarations: [
BinaryComponent,
FakeGifComponent,
GifPaletteComponent,
MidiComponent,
AsciiComponent,
BindecComponent,
MessageComponent,
JsonComponent,
CompareComponent,
HtmlPostComponent,
BinaryGifComponent,
BitComponent,
MemoryComponent,
BinaryParserDemoComponent,
HexdecComponent,
AngularFlagsComponent,
ColorIndexingComponent,
BitwiseComponent,
ToReadComponent
],
entryComponents: [
FakeGifComponent,
MidiComponent,
AsciiComponent,
BindecComponent,
MessageComponent,
JsonComponent,
HtmlPostComponent,
CompareComponent
],
exports: [BinaryComponent]
})
export class BinaryModule {}
================================================
FILE: apps/kirjs/src/app/modules/binary/bindec/bindec.component.css
================================================
.powers {
display: flex;
font-size: 5vw;
justify-content: center;
}
.power {
width: 12.5%;
border: 1px #ddd dotted;
text-align: center;
cursor: pointer;
}
.power:hover {
box-shadow: 0 0 12px 6px #999;
background: #eeeeee;
}
.power input {
zoom: 4;
}
.dec {
font-size: 3vw;
color: #444;
}
.bin {
font-size: 10vw;
}
.number {
width: 100%;
text-align: center;
font-size: 16vw;
padding: 2vw;
}
.link:hover {
color: #90cd79;
}
.link {
border-bottom: 1px #999 dotted;
margin-right: 20px;
cursor: pointer;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/bindec/bindec.component.html
================================================
{{ sign ? '-' : '+' }}
{{ sign ? '1' : '0' }}
{{ getBaseValue(i) }}
{{ v ? '1' : '0' }}
1
2
4
8
Add one
Remove one
Add sign
Remove sign
================================================
FILE: apps/kirjs/src/app/modules/binary/bindec/bindec.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BindecComponent } from './bindec.component';
describe('BindecComponent', () => {
let component: BindecComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BindecComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BindecComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/bindec/bindec.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-bindec',
templateUrl: './bindec.component.html',
styleUrls: ['./bindec.component.css']
})
export class BindecComponent implements OnInit {
digits = [0];
result = [0];
displaySign = false;
sign = false;
constructor() {}
get size() {
return this.digits.length;
}
get convertedValue() {
return (
(this.sign ? -1 : 1) *
this.result.reduce(
(result, value, index) => result + value * this.getBaseValue(index),
0
)
);
}
getBaseValue(i: number) {
return 2 ** (this.size - i - 1);
}
update(value) {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/bit/bit.component.css
================================================
.bits {
display: flex;
margin-bottom: 4vw;
}
.size-16 .bit {
font-size: 5vw;
}
.size-24 .bit {
font-size: 4vw;
}
.size-32 .bit {
font-size: 2.5vw;
}
.bit {
font-size: 10vw;
margin-left: 1vw;
color: #444;
border-bottom: 0.2vw #888 solid;
}
.label {
color: #999;
border: none;
margin-right: 2vw;
}
:host ::ng-deep .content.content.content table td {
font-size: 4vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/bit/bit.component.html
================================================
{{ bitValue.length }} bits:
{{ value }}
================================================
FILE: apps/kirjs/src/app/modules/binary/bit/bit.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BitComponent } from './bit.component';
describe('BitComponent', () => {
let component: BitComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BitComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BitComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/bit/bit.component.ts
================================================
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-bit',
templateUrl: './bit.component.html',
styleUrls: ['./bit.component.css']
})
export class BitComponent implements OnInit, OnDestroy {
bits = 7;
@Input() param = 1;
bitValue: number[] = [];
private interval = setInterval(() => {
this.generate();
}, 500);
generate() {
this.bitValue = Array.from({ length: this.param }).map(a =>
Math.round(Math.random())
);
}
ngOnDestroy() {
clearInterval(this.interval);
}
ngOnInit() {
this.generate();
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/bitwise/bitwise.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/bitwise/bitwise.component.html
================================================
bitwise works!
================================================
FILE: apps/kirjs/src/app/modules/binary/bitwise/bitwise.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BitwiseComponent } from './bitwise.component';
describe('BitwiseComponent', () => {
let component: BitwiseComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BitwiseComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BitwiseComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/bitwise/bitwise.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-bitwise',
templateUrl: './bitwise.component.html',
styleUrls: ['./bitwise.component.css']
})
export class BitwiseComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/color-indexing/color-indexing.component.css
================================================
.cell {
width: 8vw;
height: 8vw;
text-align: center;
font-size: 3vw !important;
vertical-align: middle;
line-height: 8vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/color-indexing/color-indexing.component.html
================================================
Color table (Palette)
Indexed image
================================================
FILE: apps/kirjs/src/app/modules/binary/color-indexing/color-indexing.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ColorIndexingComponent } from './color-indexing.component';
describe('ColorIndexingComponent', () => {
let component: ColorIndexingComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ColorIndexingComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ColorIndexingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/color-indexing/color-indexing.component.ts
================================================
import { Component, OnInit } from '@angular/core';
interface TableItem {
color: string;
index: number;
}
interface ColorTableHash {
[key: string]: number;
}
@Component({
selector: 'kirjs-color-indexing',
templateUrl: './color-indexing.component.html',
styleUrls: ['./color-indexing.component.css']
})
export class ColorIndexingComponent implements OnInit {
noIndexing = [
['#ff0000', '#ff0000', '#ff0000'],
['#fff000', '#ff0000', '#fff000'],
['#ff0000', '#ff0000', '#ff0000']
];
colorTable: TableItem[];
hash: ColorTableHash;
constructor() {
this.generate();
}
index() {
const index = this.noIndexing.reduce((colors, row) => {
return row.reduce((colors, cell) => {
colors[cell] = true;
return colors;
}, colors);
}, {});
return Object.keys(index).map((color, index) => ({ color, index }));
}
generate() {
this.colorTable = this.index();
this.hash = this.colorTable.reduce(
(hash: ColorTableHash, value: TableItem): ColorTableHash => {
hash[value.color] = value.index;
return hash;
},
{}
);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/compare/compare.component.css
================================================
.slide {
flex: 0 0 100vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/compare/compare.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/compare/compare.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { CompareComponent } from './compare.component';
describe('CompareComponent', () => {
let component: CompareComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CompareComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CompareComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/compare/compare.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-compare',
templateUrl: './compare.component.html',
styleUrls: ['./compare.component.css']
})
export class CompareComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/fake-gif/fake-gif.component.css
================================================
* {
font-family: Monaco, 'Lucida Console', monospace;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/fake-gif/fake-gif.component.html
================================================
This is always "87a" or "89a"
Width of the image
Height of the image
Whether global palette is present
Number of bits per primary color available
Whether the palette is sorted
Specifies number of colors in the palette proportional a power of two. e.g.
If present specifies index of a color in the global color table that would
be transparent
Ratio of the pixel
Reserved bits
Disposal Method - Indicates the way in which the graphic is to be treated
after being displayed. Values : 0 - No disposal specified. The decoder is
not required to take any action. 1 - Do not dispose. The graphic is to be
left in place. 2 - Restore to background color. The area used by the graphic
must be restored to the background color. 3 - Restore to previous. The
decoder is required to restore the area overwritten by the graphic with what
was there prior to rendering the graphic. 4-7 - To be defined.
Not used, the initial intention was to allow user interactions
Whether the frame should have a transparent color
Animation delay for next image
Optional transparent color index
Horizontal shift in pixels
Vertical shift in pixels
Width of the image
Height of the image
Whether the image has local palette
Indicates if the image is interlaced.
Whether local palette is sorted
Bucket of sizes of local palette.
Identifies the Netscape Looping Extension. This field contains the fixed
value 0x01
Size of the extension block in bytes
Number of animation loops
This is the actual image encoded with LZW
================================================
FILE: apps/kirjs/src/app/modules/binary/fake-gif/fake-gif.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FakeGifComponent } from './fake-gif.component';
describe('FakeGifComponent', () => {
let component: FakeGifComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FakeGifComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FakeGifComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/fake-gif/fake-gif.component.ts
================================================
import {
AfterViewInit,
Component,
EventEmitter,
Input,
Output,
ViewChild
} from '@angular/core';
import { BinaryParser } from '../parser/binary-parser';
import { gifParser } from './gif-parser';
import { extractMessages } from '@codelab/utils/src/lib/i18n/i18n-tools';
interface Chunk {
name: string;
size: number;
value: string;
start?: number;
}
@Component({
selector: 'kirjs-fake-gif',
templateUrl: './fake-gif.component.html',
styleUrls: ['./fake-gif.component.css']
})
export class FakeGifComponent implements AfterViewInit {
t: { [key: string]: string };
@Input()
spacing = false;
showMeta = true;
@Input() binary: string;
@Input() highlightedMap: Record = {};
@Input() highlightGroups = false;
@Input() preview = true;
@Input() filterClassName = /./;
@Input() mini = false;
@Input() showPopups = false;
@Output() binaryUpdate = new EventEmitter();
gif: string;
parser: BinaryParser;
@ViewChild('translations', { static: false }) translation;
constructor() {}
upload(file) {
const reader = new FileReader();
reader.onloadend = (e: any) => {
const result = new Uint8Array(e.target.result);
const binaries = Array.from(result)
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0));
this.binary = binaries.join('');
};
reader.readAsArrayBuffer(file.files[0]);
}
update(chunk, value) {
const len = chunk.end - chunk.start;
value = value.padEnd(len, 0).slice(0, len);
this.binary =
this.binary.slice(0, chunk.start) + value + this.binary.substr(chunk.end);
this.binaryUpdate.emit(this.binary);
}
ngAfterViewInit() {
requestAnimationFrame(() => {
this.t = extractMessages(this.translation);
this.parser = new BinaryParser().block('gif', gifParser(this.t));
});
}
updateChunk({ chunk, value }) {
this.update(chunk, value);
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/fake-gif/gif-parser.ts
================================================
import { BinaryParser } from '../parser/binary-parser';
import { lzw } from './gif';
export function gifParser(t: { [key: string]: string }) {
const header = new BinaryParser()
.string('headerConst', { length: 3, description: t.headerConst })
.string('version', { length: 3, description: t.version })
.uInt16('width', { description: t.width })
.uInt16('height', { description: t.height })
.boolean('globalPalette', { description: t.globalPalette })
.bit3('resolution', { type: 'number', description: t.resolution })
.boolean('isPaletteSorted', { description: t.isPaletteSorted })
.bit3('paletteSize', { type: 'number', description: t.paletteSize })
.uInt8('background', { description: t.background })
.uInt8('Ratio', { description: t.ratio });
const commentParser = new BinaryParser()
.string('comment', { readUntil: '00000000' })
.hex('end', { length: 2 });
const palette = new BinaryParser().array('palette', {
parser: new BinaryParser().hex('color', {
length: 6,
type: 'color'
}),
length(data) {
const paletteSize = data
.find(d => d.name === '_parent')
.value.find(d => d.name === 'header')
.value.find(d => d.name === 'paletteSize').value;
const size = parseInt(paletteSize, 2);
return 2 ** (size + 1);
}
});
const netscapeParser = new BinaryParser()
.uInt8('extensionSize', { description: t.extensionSize })
.constBits('00000001', { description: t.netscapeLoopingExtensionId })
.uInt16('loops', { description: t.loops })
.constBits('00000000', { description: '' });
const xmpParser = new BinaryParser()
.string('data', {
readUntil: '00000000'
})
.hex('end', { length: 4 });
const extensionParser = new BinaryParser()
.hex('0b', { length: 2 })
.string('type', { length: 8 })
.string('code', { length: 3 })
.choice('data', {
key: 'type',
values: {
NETSCAPE: netscapeParser,
'XMP Data': xmpParser
}
});
const graphicControlParser = new BinaryParser()
.hex('const', { length: 2 })
.constBits('000', { description: t.reservedBits })
.bit3('disposalMethod', { type: 'enums', description: t.disposalMethod })
.boolean('UI', { description: t.UI })
.boolean('isTransparent', { description: t.isTransparent })
.uInt16('delay', { description: t.delay })
.uInt8('transparentColor', { description: t.transparentColor })
.constBits('00000000');
const exclamationMarkParser = new BinaryParser()
.hex('subtype', { length: 2 })
.choice('extension', {
key: 'subtype',
values: {
f9: graphicControlParser,
ff: extensionParser,
fe: commentParser
}
});
const imageDescriptorParser = new BinaryParser()
.uInt16('left', { description: t.left })
.uInt16('top', { description: t.top })
.uInt16('imageWidth', { description: t.imageWidth })
.uInt16('imageHeight', { description: t.imageHeight })
.boolean('localPalette', { description: t.localPalette })
.boolean('isImageInterlacingEnabld', {
description: t.isImageInterlacingEnabld
})
.boolean('isLocalPaletteSorted', { description: t.isLocalPaletteSorted })
.constBits('00', { description: t.reservedBits })
.bit3('localPaletteSize', {
type: 'enums',
description: t.localPaletteSize
})
.uInt8('colorDepth')
.uInt8('blockSize')
.bit('graphicBlock', {
description: t.graphicBlock,
length: fields => {
return (
(Object as any).values(fields).find(a => a.name === 'blockSize')
.value * 8
);
},
converter(bits) {
return lzw(
2,
bits.match(/.{8}/g).map(a => parseInt(a, 2)),
4
);
}
})
.constBits('00000000');
const body = new BinaryParser()
.string('marker', { length: 1 })
.choice('extension', {
key: 'marker',
values: {
'!': exclamationMarkParser,
';': new BinaryParser(),
',': imageDescriptorParser
}
});
return new BinaryParser()
.block('header', header)
.block('palette', palette)
.array('extensions', { parser: body, length: 200 });
}
================================================
FILE: apps/kirjs/src/app/modules/binary/fake-gif/gif.ts
================================================
export function lzw(minCodeSize, data, pixelCount) {
const MAX_STACK_SIZE = 4096;
const nullCode = -1;
const npix = pixelCount;
let available,
clear,
code_mask,
code_size,
end_of_information,
in_code,
old_code,
bits,
code,
i,
datum,
data_size,
first,
top,
bi,
pi;
const dstPixels = new Array(pixelCount);
const prefix = new Array(MAX_STACK_SIZE);
const suffix = new Array(MAX_STACK_SIZE);
const pixelStack = new Array(MAX_STACK_SIZE + 1);
// Initialize GIF data stream decoder.
data_size = minCodeSize;
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = nullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
prefix[code] = 0;
suffix[code] = code;
}
// Decode GIF pixel stream.
datum = bits = first = top = pi = bi = 0;
for (i = 0; i < npix; ) {
if (top === 0) {
if (bits < code_size) {
// get the next byte
datum += data[bi] << bits;
bits += 8;
bi++;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if (code > available || code === end_of_information) {
break;
}
if (code === clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = nullCode;
continue;
}
if (old_code === nullCode) {
pixelStack[top++] = suffix[code];
old_code = code;
first = code;
continue;
}
in_code = code;
if (code === available) {
pixelStack[top++] = first;
code = old_code;
}
while (code > clear) {
pixelStack[top++] = suffix[code];
code = prefix[code];
}
first = suffix[code] & 0xff;
pixelStack[top++] = first;
// add a new string to the table, but only if space is available
// if not, just continue with current table until a clear code is found
// (deferred clear code implementation as per GIF spec)
if (available < MAX_STACK_SIZE) {
prefix[available] = old_code;
suffix[available] = first;
available++;
if ((available & code_mask) === 0 && available < MAX_STACK_SIZE) {
code_size++;
code_mask += available;
}
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
dstPixels[pi++] = pixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
dstPixels[i] = 0; // clear missing pixels
}
return dstPixels;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/gif-palette/gif-palette.component.css
================================================
* {
font-family: Monaco, 'Lucida Console', monospace;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/gif-palette/gif-palette.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/gif-palette/gif-palette.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { GifPaletteComponent } from './gif-palette.component';
describe('GifPaletteComponent', () => {
let component: GifPaletteComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [GifPaletteComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(GifPaletteComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/gif-palette/gif-palette.component.ts
================================================
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
interface Chunk {
name: string;
size: number;
value: string;
start?: number;
}
@Component({
selector: 'kirjs-gif-palette',
templateUrl: './gif-palette.component.html',
styleUrls: ['./gif-palette.component.css']
})
export class GifPaletteComponent implements OnInit {
@Output() change = new EventEmitter();
colors: number[][];
private _value = '';
get value() {
return this._value;
}
@Input()
set value(val: string) {
this._value = val;
this.colors = Array.from(val.match(/.{24}/g)).map(a =>
Array.from(a.match(/.{8}/g)).map(str => parseInt(str, 2))
);
}
serialize() {
this._value = this.colors
.map(c =>
c
.map(p => ((+p).toString(2) as any).padStart(8, 0).slice(0, 8))
.join('')
)
.join('');
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/hexdec/hexdec.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/hexdec/hexdec.component.html
================================================
Converting bin to hex
================================================
FILE: apps/kirjs/src/app/modules/binary/hexdec/hexdec.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HexdecComponent } from './hexdec.component';
describe('HexdecComponent', () => {
let component: HexdecComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [HexdecComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HexdecComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/hexdec/hexdec.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-hexdec',
templateUrl: './hexdec.component.html',
styleUrls: ['./hexdec.component.css']
})
export class HexdecComponent implements OnInit {
numbers = new Array(16).fill(0).map((a, i) => ({
bin: i.toString(2).padStart(4, '0'),
hex: i.toString(16)
}));
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/html-post/html-post.component.css
================================================
:host {
padding: 2vw;
display: block;
zoom: 3;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/html-post/html-post.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/html-post/html-post.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HtmlPostComponent } from './html-post.component';
describe('HtmlPostComponent', () => {
let component: HtmlPostComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [HtmlPostComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HtmlPostComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/html-post/html-post.component.ts
================================================
import { Component, Input } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({
selector: 'kirjs-html-post',
templateUrl: './html-post.component.html',
styleUrls: ['./html-post.component.css']
})
export class HtmlPostComponent {
html: SafeHtml;
constructor(private sanitizer: DomSanitizer) {
this.html = sanitizer.bypassSecurityTrustHtml('');
}
@Input()
set param(html: string) {
this.html = this.sanitizer.bypassSecurityTrustHtml(html);
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/json/json.component.html
================================================
Data as JSON ({{ codeLength }} bytes )
Binary data ({{ binariesLength }} bytes )
{{ error }}
{{ item.binary }}
{{ item.type }}
{{ l.value }}
{{ l.bin }}
{{
item.value
}}
{{ item.binary }}
{{
item.value
}}
{{ item.binary }}
Schema ({{ schemaLength }} bytes )
{{ item.value }}
Next
================================================
FILE: apps/kirjs/src/app/modules/binary/json/json.component.scss
================================================
.wrapper {
font-size: 3vw;
word-break: break-all;
}
.code {
white-space: pre-wrap;
font-size: 2vw;
margin-right: 2vw;
margin-left: 1vw;
}
.binary .selected {
display: block;
box-shadow: 0 0 4px 1px #444;
background: #eee;
font-size: 3vw;
}
.code .selected {
color: #444;
font-size: 3vw;
box-shadow: 0 0 4px 1px #444;
background: #eee;
}
.detail {
display: none;
}
.selected .detail {
display: block;
}
.selected .value {
display: none;
}
:host ::ng-deep {
.highlight-0 {
background: rgba(255, 124, 0, 0.47);
}
.highlight-1 {
background: rgba(255, 239, 0, 0.47);
}
.highlight-2 {
background: #00ff29;
}
.highlight-3 {
background: #00ffe7;
}
.highlight-4 {
background: rgba(0, 177, 255, 0.46);
}
.highlight-5 {
background: rgba(185, 0, 255, 0.29);
}
.highlight-6 {
background: rgba(255, 0, 89, 0.47);
}
.highlight-7 {
background: rgba(255, 115, 0, 0.4);
}
}
h2 {
font-size: 3vw !important;
}
.error::before {
color: #e51400;
content: '🙀';
}
.error {
color: #e51400;
border: 1px #e51400 solid;
border-radius: 12px;
font-size: 3vw;
padding: 2vw;
display: block;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/json/json.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { JsonComponent } from './json.component';
describe('JsonComponent', () => {
let component: JsonComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [JsonComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(JsonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/json/json.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
function strToBin(s: string) {
return Array.from(new TextEncoder().encode(s))
.map(a => a.toString(2).padStart(8, '0'))
.join('');
}
@Component({
selector: 'kirjs-json',
templateUrl: './json.component.html',
styleUrls: ['./json.component.scss']
})
export class JsonComponent implements OnInit {
@Input() code = `{
"name": "Sarah",
"test": true,
"something": 1212
}`;
match = [];
index = 0;
binaries: any[];
binariesLength: number;
codeLength: number;
schema: { value: string }[] = [];
schemaLength: number;
error: string;
constructor() {}
handleLineChange({ value: code, lineNumber }) {
this.binaries = [
{
binary: '',
comment: `we don't need to encode curly braces :)`
}
];
let val;
try {
val = JSON.parse(code);
this.error = '';
} catch (e) {
this.error = e.message;
return;
}
this.binaries = this.binaries.concat(
Object.keys(val).map(key => {
const value = val[key];
const data: any = {};
data.key = key;
data.key = key;
if (typeof value === 'boolean') {
data.binary = Number(value);
data.type = 'boolean';
data.comment = 'just one bit!';
} else if (typeof value === 'number') {
data.binary = value
.toString(2)
.padStart(Math.ceil(Math.log2(value + 1) / 8) * 8, '0');
data.type = 'number';
data.comment = 'Number';
} else if (typeof value === 'string') {
data.binary = strToBin(value) + '0000000000000000';
data.display = value
.split('')
.map(value => ({
value,
bin: (value.charCodeAt(0).toString(2) as any).padStart(8, 0)
}))
.concat({ value: 'Separator', bin: '000000000000000000' });
data.type = 'string';
data.comment = 'String!';
}
data.value = value;
return data;
})
);
this.schema = [
{
value: 'message {',
className: ''
}
]
.concat(
this.binaries.slice(1).map((b, i) => ({
value: ` ${b.type} ${b.key} = ${i};`,
className: 'highlight-' + i
}))
)
.concat({
value: '}',
className: ''
});
this.schemaLength = this.schema.map(s => s.value).join('').length;
this.match = code
.split('\n')
.map((a, i) => ({ match: a.trim(), className: `highlight-${i}` }));
this.index = lineNumber - 1;
this.codeLength = code.replace(/\s/g, '').length;
this.binariesLength = Math.ceil(
this.binaries
.map(a => a.binary.toString().length)
.reduce((a, b) => a + b, 0) / 8
);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/memory/memory.component.css
================================================
.memory {
display: flex;
flex-wrap: wrap;
width: 100%;
flex-shrink: 0;
}
.cell {
font-size: 1vw;
color: #999;
width: 5.7vw;
box-sizing: border-box;
margin-right: 0.3vw;
height: 6vw;
background: #ddd;
margin-bottom: 0.5vw;
padding: 0.5vw;
border: 1px #999 solid;
}
.value {
font-size: 3vw;
}
.index {
font-size: 1.5vw;
vertical-align: top;
padding: 0.2vw;
color: #444;
width: 2vw;
display: inline-block;
}
.cell.cell-empty {
background: #ffffff;
}
.cell.cell-boolean {
background: #444444;
color: #bbb;
}
.cell.selected2.selected2.selected2,
.cell.cell-selected2 {
background: #ff9900;
color: #444;
}
.cell.cell-number.selected,
.cell.cell-selected.cell-selected.cell-selected {
background: #ffff00;
color: #444;
}
.cell.cell-number-end-highlight,
.cell.cell-number-highlight {
background: #ff0;
color: #444;
}
.cell.cell-cell-number-end,
.cell.cell-number {
background: #444444;
color: #bbb;
}
.cell.cell-cell-number-end .index,
.cell.cell-number .index {
color: #999;
}
.cell.cell-number.cell-selected,
.cell.cell-number-highlight,
.cell.cell-number {
width: 6vw;
margin-right: 0;
border-right: 0;
border-left-style: dotted;
}
.number.number {
background: #9f0;
color: black;
}
.bool.bool {
background: #f90;
color: black;
}
.link.link {
background: #00c8ff;
color: black;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/memory/memory.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/memory/memory.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MemoryComponent } from './memory.component';
describe('MemoryComponent', () => {
let component: MemoryComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MemoryComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MemoryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/memory/memory.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-memory',
templateUrl: './memory.component.html',
styleUrls: ['./memory.component.css']
})
export class MemoryComponent implements OnInit {
@Input() param = 0;
@Input() code = '';
start = 0;
memory = Array.from({ length: 64 }).fill({
value: 0,
type: 'empty'
}) as any;
constructor() {}
allocValues(values, classes = []) {
let i = 0;
for (let x = 0; x < values.length; x++) {
const v = values[x];
for (let y = 0; y < v.length; y++) {
this.memory[i] = {
value: v[y],
type:
(y === v.length - 1 ? 'cell-number-end' : 'number') +
' ' +
(classes[x] || '')
};
i++;
}
}
}
highlightBoolean(i: number) {
this.memory[i].type =
(this.memory[i].type || '') + ' selected cell-selected';
}
highlightShouldBe(i: number) {
this.memory[i].type = (this.memory[i].type || '') + ' selected2';
}
highlightNumber(i: number) {
for (let y = 0; y < 8; y++) {
this.memory[i * 8 + y] = {
value: Math.round(Math.random()),
type: y === 7 ? 'number-end-highlight' : 'number-highlight'
};
}
}
ngOnInit() {
if (this.param === 0) {
return;
}
if (this.param === 1) {
this.allocValues('00000'.split(''));
}
if (this.param === 2) {
this.allocValues('00000'.split(''));
this.highlightBoolean(3);
}
if (this.param === 3) {
this.allocValues([
'00000000',
'00000000',
'00000000',
'00000000',
'00000000'
]);
}
if (this.param === 4) {
this.allocValues([
'00000000',
'00000000',
'00000000',
'00000000',
'00000000'
]);
this.highlightNumber(3);
}
if (this.param === 5) {
this.allocValues('10101'.split(''));
this.highlightBoolean(3);
}
const bools = '10101'.split('');
const num = '00000011';
bools[1] = num;
if (this.param === 6) {
this.allocValues(bools);
this.highlightBoolean(3);
this.highlightShouldBe(10);
}
if (this.param === 7) {
const typedArray = [
'001',
'1',
'010',
num,
'001',
'0',
'001',
'0',
'001',
'1',
'000'
];
this.allocValues(typedArray, [
' bool',
'',
' number',
'',
' bool',
'',
' bool',
' cell-selected ',
' bool'
]);
}
const typedArray = [
'011110',
'010010',
'101101',
'110001',
'110101',
'001',
'1',
'010',
num,
'001',
'0',
'001',
'0',
'001',
'1'
];
if (this.param === 8) {
this.allocValues(typedArray, [
' link',
' link',
' link',
' link',
' link',
' bool',
'',
' number',
'',
' bool',
'',
' bool',
'',
' bool'
]);
}
if (this.param === 9) {
const typedArrayWidhBool = [...typedArray, '001', '0'];
typedArrayWidhBool[1] = '111001';
this.allocValues(typedArrayWidhBool, [
' link',
' link',
' link',
' link highlight',
' link',
' bool',
'',
' bool',
'',
' bool',
'',
' bool',
'',
' bool',
'',
' bool'
]);
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/message/message.component.css
================================================
.blocks {
display: flex;
flex-wrap: wrap;
}
.block {
font-size: 1.5vw;
color: #444;
}
.basic .block {
padding: 0;
font-size: 3vw;
}
.bytes .block {
font-size: 3vw;
}
.human {
font-size: 4vw;
}
.block {
padding: 1vw;
text-align: center;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/message/message.component.html
================================================
{{ block.bin }}
{{ block.human }}
================================================
FILE: apps/kirjs/src/app/modules/binary/message/message.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MessageComponent } from './message.component';
describe('MessageComponent', () => {
let component: MessageComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MessageComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MessageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/message/message.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
function toByte(message) {
return Array.from(message.match(/.{8}/gi)).map(bin => ({
className: 'basic',
bin
}));
}
const transforms = {
basic: toByte,
bytes: toByte,
boolean(message) {
return message.split('').map(bin => {
return {
className: 'basic',
bin,
human: (!!Number(bin)).toString()
};
});
},
uint16(message) {
return Array.from(message.match(/.{16}/gi)).map(bin => ({
className: 'basic',
bin,
human: parseInt(bin as string, 2)
}));
},
uint17(message) {
return Array.from(message.match(/.{17}/gi)).map(bin => ({
className: 'basic',
bin,
human: parseInt(bin as string, 2)
}));
},
uint32(message) {
return Array.from(message.match(/.{32}/gi)).map(bin => ({
className: 'basic',
bin,
human: parseInt(bin as string, 2)
}));
},
hex(message) {
return Array.from(message.match(/.{8}/gi)).map((bin: string) => ({
className: 'basic',
bin,
human: parseInt(bin, 2).toString(16)
}));
},
uint8(message) {
return Array.from(message.match(/.{8}/gi)).map(bin => ({
className: 'basic',
bin,
human: parseInt(bin as string, 2)
}));
},
int8(message) {
return Array.from(message.match(/.{8}/gi)).map(bin => {
const sign = !!(Number(bin) & 128) ? 1 : -1;
return {
className: 'basic',
bin,
human: sign * (Number(bin) & 127)
};
});
},
string(message) {
return Array.from(message.match(/.{8}/gi)).map((bin: string) => ({
className: 'basic',
bin,
human: String.fromCharCode(parseInt(bin, 2))
}));
}
};
@Component({
selector: 'kirjs-message',
templateUrl: './message.component.html',
styleUrls: ['./message.component.css']
})
export class MessageComponent implements OnInit {
message =
'0100111001100101011101100110010101110010001000000110011101101111011011100110111001100001001000000110011' +
'101101001011101100110010100100000011110010110111101110101001000000111010101110000001000000100111001100101011101' +
'100110010101110010001000000110011101101111011011100110111001100001001000000110110001100101011101000010000001111' +
'00101101111011101010010000001100100011011110111011101101110';
mode = 1;
display = 'boolean';
blocks = transforms[this.display](this.message);
constructor() {}
@Input()
set param(value: string) {
this.setDisplay(value);
}
setDisplay(value: string) {
this.display = value;
this.blocks = transforms[this.display](this.message);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/midi/midi.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/midi/midi.component.html
================================================
meta
================================================
FILE: apps/kirjs/src/app/modules/binary/midi/midi.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MidiComponent } from './midi.component';
describe('MidiComponent', () => {
let component: MidiComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MidiComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MidiComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/midi/midi.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { BinaryParser } from '../parser/binary-parser';
import { BinaryReaderResult } from '../parser/readers/abstract-reader';
import { StringBinaryReader } from '../parser/readers/string-reader';
import { StringParser } from '../parser/parsers/string-parser';
@Component({
selector: 'kirjs-midi',
templateUrl: './midi.component.html',
styleUrls: ['./midi.component.css']
})
export class MidiComponent implements OnInit {
showMeta = true;
binary: string;
parser: BinaryParser;
s: BinaryReaderResult;
constructor() {}
updateBinary(e: Event) {}
ngOnInit() {
this.binary = localStorage.getItem('midi');
const header = new BinaryParser()
.string('headerConst', { length: 4 })
.uInt32('6')
.uInt16('Single multi-channel track')
.uInt16('Number of tracs')
.uInt16('Time-code-based time');
const timeSignatureParser = new BinaryParser()
.uInt8('upper')
.uInt8('lower')
.uInt8('clocks')
.uInt8('something');
const theEnd = new BinaryParser().uInt8('00');
const metaParser = new BinaryParser()
.uInt8('subtype')
.uInt8('length')
.choice('value', {
parser(data) {
const type = (Object as any)
.values(data)
.find(l => l.name === 'subtype').rawValue;
const length = (Object as any)
.values(data)
.find(l => l.name === 'length').value;
const parsers = {
'00000011': new StringParser({ length }),
'00000010': new StringParser({ length }),
'01011000': timeSignatureParser,
// tempo
'01010001': new BinaryParser().uInt24('value'),
'00101111': theEnd
};
if (parsers[type]) {
return parsers[type];
}
// tslint:disable-next-line:no-debugger
debugger;
}
});
const noteSwitch = new BinaryParser()
.uInt8('note number')
.uInt8('velocity');
const instrumentChannel = new BinaryParser().uInt8('instrument');
const track = new BinaryParser()
.varuint7('delta')
.uInt8('type')
.choice('typeData', {
parser: data => {
const type = (Object as any).values(data).find(l => l.name === 'type')
.rawValue;
const parsers = {
'11111111': metaParser,
'11000000': instrumentChannel,
'10010000': noteSwitch,
'10000000': noteSwitch
};
if (parsers[type]) {
return parsers[type];
}
// tslint:disable-next-line:no-debugger
debugger;
}
});
const tracks = new BinaryParser()
.string('headerConst', { length: 4 })
.uInt32('tracklen')
.array('tracks', { parser: track, length: 12 });
this.parser = new BinaryParser()
.block('header', header)
.block('block', tracks);
this.s = this.parser.readOrdered(new StringBinaryReader(this.binary));
}
upload(file) {
const reader = new FileReader();
reader.onloadend = (e: ProgressEvent) => {
const result = new Uint8Array((e.target as any).result);
const binaries = Array.from(result)
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0));
this.binary = binaries.join('');
localStorage.setItem('midi', this.binary);
};
reader.readAsArrayBuffer(file.files[0]);
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/binary-parser.spec.ts
================================================
import { StringBinaryReader } from './readers/string-reader';
import { BinaryParser } from './binary-parser';
describe('BinaryParser', () => {
beforeEach(() => {
const s = 'Universal Serial Bus'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.reader = new StringBinaryReader(s);
});
describe('BinaryParser', () => {
it('allows shortcuts', () => {
const parser = new BinaryParser()
.string('u', { length: 3 })
.bit1('a')
.bit1('b');
expect(parser.read(this.reader, {}).value).toEqual({
a: '0',
b: '1',
u: 'Uni'
});
});
it('allows nesting', () => {
const header = new BinaryParser().bit1('a').bit1('b');
const parser = new BinaryParser()
.block('header', header)
.bit1('c')
.bit1('d');
expect(parser.read(this.reader).value).toEqual({
header: { a: '0', b: '1' },
c: '0',
d: '1'
});
});
it('tracks position', () => {
const header = new BinaryParser().bit1('a').bit1('b');
const parser = new BinaryParser()
.block('header', header)
.bit1('c')
.bit1('d');
const result = parser.readOrdered(this.reader).value;
expect(result).toEqual([
{
start: 0,
end: 2,
length: 2,
name: 'header',
value: [
{
start: 0,
end: 1,
length: 1,
name: 'a',
value: '0',
rawValue: '0',
type: 'bits'
},
{
start: 1,
end: 2,
length: 1,
name: 'b',
value: '1',
rawValue: '1',
type: 'bits'
}
],
rawValue: '01',
type: 'object'
},
{
start: 2,
end: 3,
length: 1,
name: 'c',
value: '0',
rawValue: '0',
type: 'bits'
},
{
start: 3,
end: 4,
length: 1,
name: 'd',
value: '1',
rawValue: '1',
type: 'bits'
}
]);
});
it('allows uint16', () => {
const parser = new BinaryParser().uInt16('u');
expect(parser.read(this.reader).value).toEqual({ u: 28245 });
});
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/binary-parser.ts
================================================
import { BinaryObjectParser } from './parsers/object-parser';
import { StringParser, StringParserConfig } from './parsers/string-parser';
import { BinaryChoiceParser } from './parsers/choice-parser';
import { BinaryArrayParser } from './parsers/array-parser';
import { BitParser } from './parsers/bit-parser';
import { VarUintParser, VarUintParserConfig } from './parsers/var-uint-parser';
import { BinaryReader } from './readers/abstract-reader';
import { beToLe32 } from './parsers/common';
export interface BaseConfig {
description?: string;
length?: number;
type?: string;
converter?: (a: string) => number;
enum?: { [key: string]: string };
}
export class BinaryParser {
type = 'object';
private parser: BinaryObjectParser;
constructor() {
this.parser = new BinaryObjectParser();
}
string(name: string, config: StringParserConfig) {
this.parser.addStep(name, new StringParser(config));
return this;
}
varuint7(name: string, config: Partial = {}) {
this.parser.addStep(name, new VarUintParser(config));
return this;
}
varuint31(name: string, config: Partial = {}) {
this.parser.addStep(
name,
new VarUintParser({
...config,
size: 31
})
);
return this;
}
choice(name: string, config: any) {
this.parser.addStep(name, new BinaryChoiceParser({ ...config }));
return this;
}
array(name: string, config: any) {
this.parser.addStep(name, new BinaryArrayParser({ ...config }));
return this;
}
bit(name: string, config: any) {
this.parser.addStep(name, new BitParser({ ...config }));
return this;
}
block(name: string, parser) {
this.parser.addStep(name, parser);
return this;
}
constBits(value, config?: Partial) {
return this.bit('const', {
length: value.length,
type: 'const',
...config
});
}
boolean(name: string, config?: Partial) {
return this.bit(name, { length: 1, type: 'boolean', ...config });
}
bit1(name: string, config?: Partial) {
return this.bit(name, { length: 1, ...config });
}
bit2(name: string, config?: Partial) {
return this.bit(name, { length: 2, ...config });
}
bit3(name: string, config?: Partial) {
return this.bit(name, { length: 3, ...config });
}
bit8(name: string, config?: Partial) {
return this.bit(name, { length: 8, ...config });
}
bit32(name: string, config?: Partial) {
return this.bit(name, { length: 32, ...config });
}
bit24(name: string, config?: Partial) {
return this.bit(name, { length: 24, ...config });
}
object(name: string, config?: Partial) {
return this.bit(name, { length: 1, ...config });
}
uInt16(name: string, config?: Partial) {
return this.bit(name, {
type: 'number',
length: 16,
converter: a => {
return parseInt(a.slice(8) + a.slice(0, 8), 2);
},
...config
});
}
uInt24(name: string, config: any = {}) {
return this.bit(name, {
type: 'number',
length: 24,
converter: a => {
return parseInt(a, 2);
},
...config
});
}
uInt32(name: string, config?: Partial) {
return this.bit(name, {
type: 'number',
length: 32,
converter: a => {
return parseInt(a, 2);
},
...config
});
}
uInt32le(name: string, config?: Partial) {
return this.uInt32(name, {
converter: a => {
return beToLe32(parseInt(a, 2));
},
...config
});
}
uInt8(name: string, config?: Partial) {
return this.bit(name, {
type: 'number',
subtype: 'uint8',
length: 8,
converter: a => {
return parseInt(a, 2);
},
...config
});
}
hex(name: string, config?: Partial) {
if (typeof config.length === 'function') {
// tslint:disable-next-line:no-debugger
debugger;
// TODO
}
return this.bit(name, {
type: 'hex',
converter: data => {
return Array.from(data.match(/.{4}/g))
.map(a => parseInt(a.toString(), 2))
.map(a => a.toString(16))
.join('');
},
...config,
length: config.length * 4
});
}
read(reader, data: any = {}) {
return this.parser.read(reader, data);
}
readOrdered(reader: BinaryReader, data: any = [], start = 0) {
const v = this.parser.readOrdered(reader, data, start);
return {
start: start,
...v
};
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/abstract-parser.ts
================================================
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
export abstract class AbstractBinaryParser {
type: string;
abstract read(
reader: BinaryReader,
data: BinaryReaderResult
): BinaryReaderResult;
abstract readOrdered(
reader: BinaryReader,
data: any[],
start: number
): BinaryReaderResult;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/array-parser.spec.ts
================================================
import { StringParser } from './string-parser';
import { BinaryArrayParser } from './array-parser';
import { StringBinaryReader } from '../readers/string-reader';
describe('array parser', () => {
beforeEach(() => {
const s = 'Universal Serial Bus'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.reader = new StringBinaryReader(s);
});
it('can read one letter', () => {
const parser = new BinaryArrayParser({
length: 2,
parser: new StringParser({ length: 2 })
});
expect(parser.read(this.reader).value).toEqual(['Un', 'iv']);
});
it('can read one letter', () => {
const parser = new BinaryArrayParser({
parser: new StringParser({ length: 2 })
});
expect(parser.read(this.reader).value).toEqual([
'Un',
'iv',
'er',
'sa',
'l ',
'Se',
'ri',
'al',
' B',
'us'
]);
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/array-parser.ts
================================================
import { AbstractBinaryParser } from './abstract-parser';
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
import { resolveFunctionOrvalue, resolveLengthOrdered } from '../utils';
export class BinaryArrayParser extends AbstractBinaryParser {
type = 'array';
constructor(private config) {
super();
}
read(
reader: BinaryReader,
data: BinaryReaderResult = {}
): BinaryReaderResult {
let len = resolveFunctionOrvalue(this.config.length, data) || Infinity;
let raw = '';
const value = [];
while (len > 0 && reader.hasMore()) {
const result = this.config.parser.read(reader, data);
raw += result.raw;
value.push(result.value);
len--;
}
return { value, raw };
}
readOrdered(
reader: BinaryReader,
data: BinaryReaderResult = [],
start = 0
): BinaryReaderResult {
let numberOfElements = resolveLengthOrdered(this.config.length, data);
if (numberOfElements === undefined) {
numberOfElements = Infinity;
}
let rawValue = '';
const value = [];
let len = 0;
let index = 0;
while (numberOfElements > 0 && reader.hasMore()) {
index++;
const result = this.config.parser.readOrdered(reader, data, start + len);
rawValue += result.rawValue;
const length = result.rawValue.length;
value.push({
start: start + len,
end: start + len + length,
length,
index,
type: this.config.parser.type,
value: result.value,
rawValue: result.rawValue
});
len += length;
numberOfElements--;
}
return {
name: this.config.name,
start,
length: len,
end: start + len,
value,
rawValue,
type: this.type
};
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/bit-parser.spec.ts
================================================
import { StringBinaryReader } from '../readers/string-reader';
import { BitParser } from './bit-parser';
describe('BinaryParser', () => {
beforeEach(() => {
const s = 'Universal Serial Bus'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.reader = new StringBinaryReader(s);
});
describe('bit parser', () => {
it('can read 3 bit bit', () => {
const parser = new BitParser({ length: 3 });
const results = parser.read(this.reader);
expect(results.value).toBe('010');
expect(results.rawValue).toBe('010');
});
it('takes a length function', () => {
const parser = new BitParser({ length: () => 3 });
const result = parser.read(this.reader);
expect(result.value).toBe('010');
expect(result.rawValue).toBe('010');
});
it('takes a length function which can use existing data', () => {
const parser = new BitParser({ length: data => data.len });
const result = parser.read(this.reader, { len: 3 });
expect(result.value).toBe('010');
expect(result.rawValue).toBe('010');
});
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/bit-parser.ts
================================================
import { AbstractBinaryParser } from './abstract-parser';
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
import {
resolveByKey,
resolveFunctionKeyOrValue,
resolveOrderedByKey
} from '../utils';
export class BitParser extends AbstractBinaryParser {
type = 'bits';
constructor(private config) {
super();
this.type = config.type || this.type;
}
readWithLength(
reader: BinaryReader,
data: BinaryReaderResult = [],
len: number
) {
const rawValue = reader.read(len);
const converter = this.config.converter || (a => a);
return { value: converter(rawValue), rawValue };
}
read(
reader: BinaryReader,
data: BinaryReaderResult = {}
): BinaryReaderResult {
const len = resolveFunctionKeyOrValue(
this.config.length,
data,
resolveByKey
);
return this.readWithLength(reader, data, len);
}
readOrdered(
reader: BinaryReader,
data: BinaryReaderResult = [],
start = 0
): BinaryReaderResult {
if (start === 0) {
// tslint:disable-next-line:no-debugger
debugger;
}
const len = resolveFunctionKeyOrValue(
this.config.length,
data,
resolveOrderedByKey
);
const result = this.readWithLength(reader, data, len);
const length = result.rawValue.length;
const end = start + length;
return {
displayValue:
(this.config && this.config.enum && this.config.enum[result.value]) ||
result.value,
start,
length,
end,
...result,
type: this.type,
description: this.config && this.config.description,
unconverter: this.config.unconverter
};
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/choice-parser.spec.ts
================================================
import { StringBinaryReader } from '../readers/string-reader';
import { BitParser } from './bit-parser';
import { BinaryChoiceParser } from './choice-parser';
import { StringParser } from './string-parser';
describe('BinaryParser', () => {
beforeEach(() => {
const s = 'Universal Serial Bus'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.reader = new StringBinaryReader(s);
});
describe('choice parser', () => {
it('can read one letter', () => {
const parser = new BinaryChoiceParser({
key: ({ p }) => p,
values: {
'1': new BitParser({ length: 8 }),
'2': new StringParser({ length: 2 })
}
});
expect(parser.read(this.reader, { p: '1' }).value).toEqual('01010101');
expect(parser.read(this.reader, { p: '2' }).value).toEqual('ni');
});
it('can read one letter', () => {
const parser = new BinaryChoiceParser({
key: ({ p }) => p,
values: {
'1': new BitParser({ length: 8 }),
'2': new StringParser({ length: 2 })
}
});
expect(parser.readOrdered(this.reader, { p: '1' }).value).toEqual(
'01010101'
);
expect(parser.readOrdered(this.reader, { p: '2' }).value).toEqual('ni');
});
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/choice-parser.ts
================================================
import { AbstractBinaryParser } from './abstract-parser';
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
import { resolveByKey, resolveOrderedByKey } from '../utils';
export class BinaryChoiceParser extends AbstractBinaryParser {
constructor(private config) {
super();
}
get type() {
return 'bich';
}
read(
reader: BinaryReader,
data: BinaryReaderResult = []
): BinaryReaderResult {
return this.getParser(data, resolveByKey).read(reader, data);
}
getParser(data, resolver) {
let parser: AbstractBinaryParser;
if (this.config.key) {
const keyValue = resolver(this.config.key, data);
parser = this.config.values[keyValue];
}
if (this.config.parser) {
parser = this.config.parser(data);
}
if (!parser) {
// tslint:disable-next-line:no-debugger
debugger;
}
return parser;
}
readOrdered(
reader: BinaryReader,
data: BinaryReaderResult = [],
start = 0
): BinaryReaderResult {
const parser = this.getParser(data, resolveOrderedByKey);
const { value, rawValue, description } = parser.readOrdered(
reader,
data,
start
);
return { start, value, rawValue, type: parser.type, description };
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/common.ts
================================================
export interface ParserConfig {
description?: string;
}
export enum Endianness {
BIG = 1,
LITTLE
}
export interface BinaryParserConfig {
endianness: Endianness.BIG;
}
export const defaultConfig: BinaryParserConfig = {
endianness: Endianness.BIG
};
export function beToLe32(val) {
return (
((val & 0xff) << 24) |
((val & 0xff00) << 8) |
((val >> 8) & 0xff00) |
((val >> 24) & 0xff)
);
}
export interface ReadResult {
parent: ReadResult | null;
results: ReadResult[];
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/debugger-parser.ts
================================================
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
export abstract class DebuggerParser {
type = 'debugger';
read(reader: BinaryReader, data: BinaryReaderResult) {
// tslint:disable-next-line:no-debugger
debugger;
}
readOrdered(reader: BinaryReader, data: BinaryReaderResult) {
// tslint:disable-next-line:no-debugger
debugger;
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/first-bit-parser.spec.ts
================================================
import { StringBinaryReader } from '../readers/string-reader';
import { VarUintParser } from './var-uint-parser';
fdescribe('FirstBitParse', () => {
it('can read 1 byte', () => {
const reader = new StringBinaryReader('0111111111111111');
const result = new VarUintParser().read(reader);
expect(result.rawValue).toEqual('01111111');
});
it('can read 1 byte', () => {
const reader = new StringBinaryReader('11111111');
const result = new VarUintParser().read(reader);
expect(result.rawValue).toEqual('11111111');
});
it('can read 2 bytes', () => {
const reader = new StringBinaryReader('1111111101111111111');
const result = new VarUintParser().read(reader);
expect(result.rawValue).toEqual('1111111101111111');
});
it('can read 3 bytes', () => {
const reader = new StringBinaryReader('111111111111111101111111111');
const result = new VarUintParser().read(reader);
expect(result.rawValue).toEqual('111111111111111101111111');
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/object-parser.spec.ts
================================================
import { StringBinaryReader } from '../readers/string-reader';
import { BinaryObjectParser } from './object-parser';
import { BitParser } from './bit-parser';
describe('BinaryParser', () => {
beforeEach(() => {
const s = 'Universal Serial Bus'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.reader = new StringBinaryReader(s);
});
describe('BinaryObjectParser', () => {
it('can read simple bits', () => {
const parser = new BinaryObjectParser();
parser.addStep('a', new BitParser({ length: 1 }));
parser.addStep('b', new BitParser({ length: 2 }));
parser.addStep('c', new BitParser({ length: 1 }));
const result = parser.read(this.reader);
expect(result.value).toEqual({
a: '0',
b: '10',
c: '1'
});
});
it('can read nested objects', () => {
const parser = new BinaryObjectParser();
parser.addStep('a', new BitParser({ length: 1 }));
const innerParser = new BinaryObjectParser();
innerParser.addStep('a', new BitParser({ length: 1 }));
innerParser.addStep('b', new BitParser({ length: 1 }));
parser.addStep('inner', innerParser);
parser.addStep('b', new BitParser({ length: 1 }));
const result = parser.read(this.reader);
expect(result.value).toEqual({
a: '0',
inner: {
a: '1',
b: '0'
},
b: '1'
});
});
it('can read nested objects', () => {
const parser = new BinaryObjectParser();
parser.addStep('a', new BitParser({ length: 1 }));
const innerParser = new BinaryObjectParser();
innerParser.addStep('a', new BitParser({ length: 1 }));
innerParser.addStep('b', new BitParser({ length: 1 }));
parser.addStep('inner', innerParser);
parser.addStep('b', new BitParser({ length: 1 }));
const result = parser.readOrdered(this.reader);
expect(result.value).toEqual([
{
name: 'a',
value: '0',
rawValue: '0',
type: 'bits'
},
{
name: 'inner',
value: [
{
name: 'a',
value: '1',
rawValue: '1',
type: 'bits'
},
{
name: 'b',
value: '0',
rawValue: '0',
type: 'bits'
}
],
rawValue: '10',
type: 'object'
},
{
name: 'b',
value: '1',
rawValue: '1',
type: 'bits'
}
]);
});
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/object-parser.ts
================================================
import { AbstractBinaryParser } from './abstract-parser';
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
export class BinaryObjectParser extends AbstractBinaryParser {
type = 'object';
steps: {
name: string;
description?: string;
parser: AbstractBinaryParser;
}[] = [];
addStep(name: string, parser: AbstractBinaryParser) {
this.steps.push({ name, parser });
}
read(
reader: BinaryReader,
data: BinaryReaderResult = {}
): BinaryReaderResult {
let raw = '';
const val = this.steps.reduce((result, step) => {
const { value, rawValue } = step.parser.read(reader, result);
result[step.name] = value;
raw += rawValue;
return result;
}, {});
return { value: val, rawValue: raw };
}
readOrdered(
reader: BinaryReader,
data: BinaryReaderResult = [],
start = 0
): BinaryReaderResult {
let raw = '';
let len = 0;
const value = this.steps.reduce((result, step) => {
const {
value,
rawValue,
type,
description,
displayValue
} = step.parser.readOrdered(
reader,
[...result, { name: '_parent', value: data }],
start + len
);
raw += rawValue;
if (!type) {
// tslint:disable-next-line:no-debugger
debugger;
}
len += rawValue.length;
result.push({
start: start + len - rawValue.length,
end: start + len,
length: rawValue.length,
name: step.name,
description,
value,
displayValue,
rawValue,
type
});
return result;
}, []);
return {
start,
length: len,
end: start + len,
value,
rawValue: raw,
type: 'object'
};
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/string-parser.spec.ts
================================================
import { BinaryParser } from '../binary-parser';
import { StringParser } from './string-parser';
import { StringBinaryReader } from '../readers/string-reader';
describe('BinaryParser', () => {
beforeEach(() => {
const s = 'Universal Serial Bus'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
this.reader = new StringBinaryReader(s);
});
fdescribe('string parser', () => {
it('can read one letter', () => {
const parser = new StringParser({ length: 1 });
expect(parser.read(this.reader).value).toBe('U');
});
it('can read 3 letttes', () => {
const parser = new StringParser({ length: 3 });
expect(parser.read(this.reader).value).toBe('Uni');
});
describe('readuntil', () => {
const s = 'lollol'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
const s2 = 'ogogog'
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2))
.map(a => (a as any).padStart(8, 0))
.join('');
it('can read until 00', () => {
this.reader = new StringBinaryReader(s + '00000000' + s2);
const parser = new StringParser({ readUntil: '00000000' });
const binaryReaderResult = parser.readOrdered(this.reader);
expect(binaryReaderResult.value).toBe('lollol');
});
it('Reads the whole thing if no 00', () => {
this.reader = new StringBinaryReader(s + '01010101' + s2);
const parser = new StringParser({ readUntil: '00000000' });
const binaryReaderResult = parser.readOrdered(this.reader);
expect(binaryReaderResult.value).toBe('lollolUogogog');
});
});
it('can read ordered 3 letttes', () => {
const parser = new StringParser({ length: 3 });
const binaryReaderResult = parser.readOrdered(this.reader);
expect(binaryReaderResult.value).toBe('Uni');
expect(binaryReaderResult.start).toBe(0);
expect(binaryReaderResult.end).toBe(24);
expect(binaryReaderResult.length).toBe(24);
});
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/string-parser.ts
================================================
import { AbstractBinaryParser } from './abstract-parser';
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
import {
resolveFunctionKeyOrValue,
resolveFunctionOrvalue,
resolveLengthOrdered
} from '../utils';
import { ParserConfig } from './common';
function bytesToChar(a) {
return String.fromCharCode(parseInt(a, 2));
}
export interface StringParserConfig extends ParserConfig {
length?: number | Function | string;
readUntil?: string;
}
export class StringParser extends AbstractBinaryParser {
type = 'string';
constructor(private config: StringParserConfig) {
super();
}
read(
reader: BinaryReader,
data: BinaryReaderResult = {},
start = 0
): BinaryReaderResult {
if (this.config.readUntil) {
let value = '';
let rawValue = '';
while (
reader.peak(this.config.readUntil.length) !== this.config.readUntil &&
reader.peak(this.config.readUntil.length) > 0
) {
const letter = reader.read(8);
value += bytesToChar(letter);
rawValue += letter;
}
return { value, rawValue };
} else {
const len = resolveLengthOrdered(this.config.length, data) * 8;
const rawValue = reader.read(len);
const value = rawValue
.match(/.{8}/g)
.map(bytesToChar)
.join('');
return { value, rawValue };
}
}
readOrdered(
reader: BinaryReader,
data: BinaryReaderResult = [],
start = 0
): BinaryReaderResult {
const result = this.read(reader, data);
const length = start + result.rawValue.length;
const end = start + length;
return {
...result,
type: this.type,
description: this.config.description,
start,
end,
length
};
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/parsers/var-uint-parser.ts
================================================
import { AbstractBinaryParser } from './abstract-parser';
import { BinaryReader, BinaryReaderResult } from '../readers/abstract-reader';
import { BaseConfig } from '../binary-parser';
export interface VarUintParserConfig extends BaseConfig {
size?: number;
}
export const defaultVarUintParserConfig = {
size: 7
};
export class VarUintParser extends AbstractBinaryParser {
type = 'bits';
constructor(
private config: VarUintParserConfig = defaultVarUintParserConfig
) {
super();
this.type = config.type || this.type;
}
static converter(n: string) {
return parseInt(n, 2);
}
read(
reader: BinaryReader,
data: BinaryReaderResult = {}
): BinaryReaderResult {
let hasNext = true;
let value = '';
let rawValue = '';
const size = this.config.size || 7;
let i = 100;
while (hasNext) {
const firstBit = reader.read(1);
hasNext = !!+firstBit;
rawValue += firstBit;
const result = reader.read(size);
value += result;
rawValue += result;
if (i-- < 1) {
// tslint:disable-next-line:no-debugger
debugger;
}
}
const converter = this.config.converter || VarUintParser.converter;
return { value: converter(rawValue), rawValue };
}
readOrdered(
reader: BinaryReader,
data: BinaryReaderResult = [],
start = 0
): BinaryReaderResult {
return { ...this.read(reader, data), type: this.type };
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/readers/abstract-reader.ts
================================================
export type BinaryReaderResult = any;
export abstract class BinaryReader {
abstract read(bits: number);
abstract peak(bits: number);
abstract hasMore(): boolean;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/readers/string-reader.ts
================================================
import { BinaryReader, BinaryReaderResult } from './abstract-reader';
export class StringBinaryReader implements BinaryReader {
private index = 0;
constructor(private s: string) {}
hasMore() {
return this.index < this.s.length;
}
peak(bits: number): BinaryReaderResult {
return this.s.slice(this.index, this.index + bits);
}
read(bits: number): BinaryReaderResult {
this.index += bits;
const result = this.s.slice(this.index - bits, this.index);
return result;
}
}
================================================
FILE: apps/kirjs/src/app/modules/binary/parser/utils.ts
================================================
export function resolveLengthOrdered(functionOrValue, data) {
if (typeof functionOrValue === 'string') {
return resolveOrderedByKey(functionOrValue, data);
}
return typeof functionOrValue === 'function'
? functionOrValue(data)
: functionOrValue;
}
export function resolveFunctionOrvalue(functionOrValue, arg) {
return typeof functionOrValue === 'function'
? functionOrValue(arg)
: functionOrValue;
}
export function resolveFunctionKeyOrValue(val, data, resolve) {
if (typeof val === 'string') {
return resolve(val, data);
}
if (typeof val === 'function') {
return val(data, resolve);
}
return val;
}
export function resolveOrderedByKey(key: string, data: any[]) {
return Object.values(data).find(a => a.name === key).value;
}
export function resolveByKey(key: string, data: any) {
return data[key];
}
export function strToBin(str) {
return str
.split('')
.map(a => a.charCodeAt(0))
.map(a => a.toString(2).padStart(8, 0))
.join('');
}
================================================
FILE: apps/kirjs/src/app/modules/binary/shared.ts
================================================
export function bin2hex(bin: string) {
return bin
.match(/.{8}/gi)
.map(a => (parseInt(a, 2).toString(16) as any).padStart(2, 0))
.join('');
}
================================================
FILE: apps/kirjs/src/app/modules/binary/to-read/to-read.component.css
================================================
h2 {
font-size: 5vw !important;
line-height: 5vw !important;
}
h3 {
font-size: 3vw;
color: #444;
}
:host ::ng-deep .book {
width: 40vw;
height: 100vw;
background-repeat: no-repeat;
background-size: 40vw;
}
================================================
FILE: apps/kirjs/src/app/modules/binary/to-read/to-read.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/binary/to-read/to-read.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ToReadComponent } from './to-read.component';
describe('ToReadComponent', () => {
let component: ToReadComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ToReadComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ToReadComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/binary/to-read/to-read.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-to-read',
templateUrl: './to-read.component.html',
styleUrls: ['./to-read.component.css']
})
export class ToReadComponent implements OnInit {
@Input() author: string;
@Input() title: string;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/board/board.component.css
================================================
.cell {
border: 1px #888 solid;
margin-left: -1px;
margin-top: -5px;
}
.cell.cell-x {
border: none;
}
.cell-1 {
background: black;
}
.board,
.cell {
display: inline-block;
}
.line {
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/board/board.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/board/board.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BoardComponent } from './board.component';
describe('BoardComponent', () => {
let component: BoardComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BoardComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BoardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/board/board.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-board',
templateUrl: './board.component.html',
styleUrls: ['./board.component.css']
})
export class BoardComponent implements OnInit {
@Input() pattern;
@Input() cellHeight = 50;
@Input() cellWidth = 50;
@Input() transform;
@Input() playing = false;
@Input() delay = 500;
constructor() {}
public play() {
this.playing = true;
}
runTransform() {
if (this.playing) {
this.pattern = this.transform(this.pattern);
}
setTimeout(() => {
this.runTransform();
}, this.delay);
}
ngOnInit() {
if (this.transform) {
this.runTransform();
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/cellular-automation-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SlidesRoutes } from '@ng360/slides';
import { CellularAutomationComponent } from './cellular-automation.component';
import { CellularAutomationModule } from './cellular-automation.module';
const routes = RouterModule.forChild(
SlidesRoutes.get(CellularAutomationComponent)
);
@NgModule({
imports: [routes, CellularAutomationModule],
declarations: [],
exports: []
})
export class CellularAutomationRoutingModule {}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/cellular-automation.component.css
================================================
h2 {
font-weight: 800;
}
pre {
font-size: 24px;
}
.empty-slide {
width: 400px;
height: 250px;
border: 1px solid #000;
margin-left: 300px;
margin-top: 10px;
-webkit-box-shadow: 10px 10px 5px 0px rgba(181, 172, 181, 1);
-moz-box-shadow: 10px 10px 5px 0px rgba(181, 172, 181, 1);
box-shadow: 10px 10px 5px 0px rgba(181, 172, 181, 1);
}
.html-elements {
font-size: 28px;
margin-top: 20px;
margin-left: 24px;
}
.kirjs {
width: 800px;
height: 300px;
background: url(intro/kirjs.png) no-repeat;
background-size: 100%;
margin: 0 auto;
}
.rules {
display: flex;
}
.play:hover {
background-color: #d6510f;
}
.play {
font-size: 14px;
padding: 5px;
text-align: center;
cursor: pointer;
margin-left: 20px;
margin-top: 10px;
background-color: #000000;
color: #ffffee;
height: 30px;
width: 30px;
}
.grid-header {
margin-top: 20px;
display: flex;
}
.img-wolfram {
width: 400px;
height: 400px;
background: url('intro/wolfram.jpg');
}
.img-moore {
width: 400px;
height: 400px;
background: url('intro/moore.jpg') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-neuman {
width: 400px;
height: 400px;
background: url('intro/JohnvonNeumann.gif') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-conway {
width: 600px;
height: 400px;
background: url('intro/Conway_.jpg') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.neighborhood {
display: flex;
}
.img-gun {
width: 800px;
height: 600px;
background: url('intro/Gosperglidergun.gif') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-turing {
width: 800px;
height: 800px;
background: url('intro/turing.gif') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-mp {
width: 800px;
height: 800px;
background: url('intro/metapixel.png') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-30 {
width: 800px;
height: 800px;
background: url('intro/30.png') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-30-2 {
width: 800px;
height: 800px;
background: url('intro/30-2.png') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-gosper {
width: 800px;
height: 800px;
background: url('intro/Bill_Gosper_2006.jpg') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-cave {
width: 800px;
height: 800px;
background: url('intro/maze.png') no-repeat;
background-size: 100%;
margin-right: 50px;
}
.img-nks {
width: 800px;
height: 800px;
background: url('intro/nks.jpg') no-repeat;
background-size: 100%;
margin-right: 50px;
}
:host /deep/ .slide {
display: block;
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/cellular-automation.component.html
================================================
Grid
Stephen Wolfram (Rule 30)
Cellular Automata 2D!
Neighborhoods
John von Neumann
Extended
Bunch of demos
3d
VIDEO
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/cellular-automation.component.ts
================================================
import { Component } from '@angular/core';
function inPlace(rule) {
const map = (256 + rule)
.toString(2)
.substr(1)
.split('')
.map(Number)
.reduce((a, v, i) => {
a[(15 - i).toString(2).substr(1)] = v;
return a;
}, {});
function transformRow(row) {
const result = [];
for (let i = 0; i < row.length; i++) {
const a1 = row[(row.length + i - 1) % row.length].toString();
const a2 = row[i].toString();
const a3 = row[(i + 1) % row.length].toString();
result[i] = [map[a1 + a2 + a3]];
}
return result;
}
return function(grid) {
return grid.map(transformRow);
};
}
function transform2d(rule) {
const survive = rule.survive.reduce((a, v) => {
a[v] = true;
return a;
}, {});
const born = rule.born.reduce((a, v) => {
a[v] = true;
return a;
}, {});
return function(grid) {
return grid.map((row, y) => {
return row.map((cell, x) => {
const px = (row.length + x - 1) % row.length;
const nx = (x + 1) % row.length;
const py = (grid.length + y - 1) % grid.length;
const ny = (y + 1) % grid.length;
const score =
grid[py][px] +
grid[py][x] +
grid[py][nx] +
grid[y][px] +
grid[y][nx] +
grid[ny][px] +
grid[ny][x] +
grid[ny][nx];
if ((cell === 0 && born[score]) || (cell === 1 && survive[score])) {
return 1;
}
return 0;
});
});
};
}
function appendTransform(rule) {
const transform = inPlace(rule);
return function(grid) {
const lastRow = grid[grid.length - 1];
grid.push(transform([lastRow])[0]);
return grid;
};
}
const rand = Math.floor(Math.random() * 255);
const randomRules = new Array(8).fill(0).reduce(
(a, v, i) => {
if (Math.random() < 0.3) {
a.survive.push(i);
}
if (Math.random() < 0.3) {
a.born.push(i);
}
return a;
},
{ survive: [], born: [] }
);
const gameOfLifeRules = { survive: [2, 3], born: [3] };
const gameOfLife = transform2d(gameOfLifeRules);
const randomPattern = new Array(30)
.fill(0)
.map(() => new Array(60).fill(0).map(() => (Math.random() < 0.3 ? 1 : 0)));
const randomPatternSparce = new Array(30)
.fill(0)
.map(() => new Array(60).fill(0).map(() => (Math.random() < 0.04 ? 1 : 0)));
const labyrynthRules = { survive: [0, 1, 2, 3], born: [1] };
const labRules4 = { survive: [0, 1, 2, 3, 4], born: [1] };
const labRules5 = { survive: [0, 1, 2, 3, 4, 5], born: [1] };
const labRules6 = { survive: [0, 1, 2, 3, 4, 5, 6], born: [1] };
@Component({
selector: 'kirjs-cellular-automation',
templateUrl: './cellular-automation.component.html',
styleUrls: ['./cellular-automation.component.css']
})
export class CellularAutomationComponent {
examples = {
intro: {
inverse(pattern) {
return pattern.map(line => line.map(cell => (cell + 1) % 2));
},
rand,
inPlace16: inPlace(16),
inPlace90: inPlace(90),
twoD18: appendTransform(18),
twoD30: appendTransform(30),
twoD90: appendTransform(90),
twoD110: appendTransform(110),
twoDRand: appendTransform(rand),
pattern: [
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0]
]
},
life: {
randomRules,
random: transform2d(randomRules),
gameOfLifeRules,
gameOfLife,
labyrynthRules: labyrynthRules,
labyrynth: transform2d(labyrynthRules),
labRules4,
labyrynth4: transform2d(labRules4),
labRules5,
labyrynth5: transform2d(labRules5),
labRules6,
labyrynth6: transform2d(labRules6),
pattern: {
randomPattern,
randomPatternSparce,
stillLife: [
[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]
],
oscilator: [
[0, 0, 0, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0]
],
oscilatorClock: [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0],
[0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
glider: [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
],
copperhead: [
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
1,
1,
1,
0,
1,
1,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
1,
1,
0,
0,
0,
0,
0,
0,
1,
1,
0,
1,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
1,
1,
0,
0,
0,
0,
0,
0,
1,
1,
0,
1,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
1,
1,
1,
0,
1,
1,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
]
]
}
}
};
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/cellular-automation.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SlidesModule } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { CellularAutomationComponent } from './cellular-automation.component';
import { BoardComponent } from './board/board.component';
import { Rule3Component } from './rule3/rule3.component';
import { RuleComponent } from './rule/rule.component';
import { Rule8Component } from './rule8/rule8.component';
import { OscilatorsComponent } from './oscilators/oscilators.component';
import { Rule4Component } from './rule3/rule4/rule4.component';
@NgModule({
imports: [SlidesModule, FeedbackModule, CommonModule],
declarations: [
CellularAutomationComponent,
BoardComponent,
Rule3Component,
RuleComponent,
Rule8Component,
OscilatorsComponent,
Rule4Component
],
exports: [
CellularAutomationComponent,
CellularAutomationComponent,
BoardComponent,
Rule3Component,
RuleComponent,
Rule8Component,
OscilatorsComponent,
BoardComponent,
Rule4Component
]
})
export class CellularAutomationModule {}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/oscilators/oscilators.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/oscilators/oscilators.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/oscilators/oscilators.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OscilatorsComponent } from './oscilators.component';
describe('OscilatorsComponent', () => {
let component: OscilatorsComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [OscilatorsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OscilatorsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/oscilators/oscilators.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-oscilators',
templateUrl: './oscilators.component.html',
styleUrls: ['./oscilators.component.css']
})
export class OscilatorsComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule/rule.component.css
================================================
:host {
margin: 10px 0;
}
.rule {
margin-left: 10px;
}
.arrow {
font-size: 18px;
text-align: center;
padding: 5px;
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule/rule.component.html
================================================
⇩
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule/rule.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RuleComponent } from './rule.component';
import { BoardComponent } from '../board/board.component';
describe('RuleComponent', () => {
let component: RuleComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [RuleComponent, BoardComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RuleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule/rule.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-rule',
templateUrl: './rule.component.html',
styleUrls: ['./rule.component.css']
})
export class RuleComponent implements OnInit {
@Input() before = [];
@Input() after = [];
@Input() arrow = false;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule3.component.css
================================================
.rule {
display: flex;
width: 100%;
justify-content: space-between;
}
.index {
text-align: center;
font-size: 50px;
flex: 1;
margin-left: -20px;
}
kirjs-rule {
margin-right: 20px;
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule3.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule3.component.ts
================================================
import { Component, Input, OnChanges, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-rule3',
templateUrl: './rule3.component.html',
styleUrls: ['./rule3.component.css']
})
export class Rule3Component implements OnInit, OnChanges {
after: Array>;
before: Array>;
@Input() rule = 0;
@Input() indexes = false;
@Input() arrow = false;
constructor() {}
ngOnChanges() {
this.after = (256 + this.rule)
.toString(2)
.substr(1)
.split('')
.map(Number)
.map(a => ['x', a, 'x']);
// tslint:disable
this.before = this.after
.map((v, i) =>
(8 + i)
.toString(2)
.substr(1)
.split('')
)
.reverse();
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule4/rule4.component.css
================================================
.wrapper {
display: flex;
flex-wrap: wrap;
}
.rule {
margin-left: 10px;
margin-top: 10px;
}
.cell {
width: 20px;
height: 20px;
border: 1px #ddd solid;
}
.after {
margin-left: 20px;
}
.active {
background: lime;
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule4/rule4.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule4/rule4.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Rule4Component } from './rule4.component';
describe('Rule4Component', () => {
let component: Rule4Component;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [Rule4Component]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(Rule4Component);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule3/rule4/rule4.component.ts
================================================
import { Component, Input, OnChanges } from '@angular/core';
@Component({
selector: 'kirjs-rule4',
templateUrl: './rule4.component.html',
styleUrls: ['./rule4.component.css']
})
export class Rule4Component implements OnChanges {
@Input() test;
before: string[][];
after: number[];
ngOnChanges() {
this.after = this.test.table.map(a => (a === 'enable' ? 1 : 0));
this.before = new Array(8)
.fill(0)
.map((v, i) =>
(8 + i)
.toString(2)
.substr(1)
.split('')
)
.reverse();
}
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule8/rule8.component.css
================================================
:host {
width: 100%;
}
kirjs-rule {
margin-right: 20px;
display: flex;
flex-direction: column;
align-items: center;
float: left;
margin-bottom: 20px;
}
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule8/rule8.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/cellular-automation/rule8/rule8.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-rule8',
templateUrl: './rule8.component.html',
styleUrls: ['./rule8.component.css']
})
export class Rule8Component implements OnInit {
after: Array>>;
before: Array>>;
@Input() rule = 0;
@Input() arrow = false;
constructor() {}
ngOnInit() {
const numbers = new Array(1024).fill(0, 0, 1024);
this.after = numbers.map(a => [[Math.round(Math.random())]]);
this.before = numbers.map((v, i) => {
const arr = (1024 + i).toString(2).substr(1);
return [
arr.substr(0, 3).split(''),
arr.substr(3, 3).split(''),
arr.substr(6, 3).split('')
];
});
}
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/board/board.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/gomoku/board/board.component.scss
================================================
.column {
display: flex;
flex-grow: 1;
flex-basis: 0;
}
kirjs-tools {
display: none;
}
.board {
width: 100%;
height: 100%;
border-left: 1px #999 solid;
border-top: 1px #999 solid;
display: flex;
flex-direction: column;
}
.cell {
&.highlight-yellow {
background: #ff0;
}
&.highlight-orange {
background: #ffb900;
}
&.highlight-transparent {
opacity: 0.4;
}
flex-grow: 1;
flex-basis: 0;
border-right: 1px #999 solid;
border-bottom: 1px #999 solid;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.theme-print {
.cell-1:before {
content: 'x';
color: #c53a00;
}
.cell-2:before {
content: 'o';
color: #666;
}
.cell-1:before,
.cell-2:before {
font-size: 12px;
}
}
.theme-gomoku {
.cell-1:before {
content: '⚫';
}
.cell-2:before {
content: '🔴';
}
.cell-3:before {
content: '⚫';
opacity: 0.5;
}
.cell-4:before {
content: '🔴';
opacity: 0.5;
}
.cell-1:before,
.cell-2:before {
font-size: 28px;
}
}
.theme-xo {
.cell-1:before {
content: '×';
color: red;
}
.cell-2:before {
content: '⚬';
color: blue;
}
.cell-1:before,
.cell-2:before {
font-size: 200px;
}
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/board/board.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BoardComponent } from './board.component';
import { ToolsComponent } from '../tools/tools.component';
describe('BoardComponent', () => {
let component: BoardComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [BoardComponent, ToolsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BoardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/gomoku/board/board.component.ts
================================================
import { Component, HostListener, Input } from '@angular/core';
import { Gomoku } from 'gomoku-tools';
import { Highlights } from '../highlights';
@Component({
selector: 'kirjs-board',
templateUrl: './board.component.html',
styleUrls: ['./board.component.scss']
})
export class BoardComponent {
@Input() theme = 'gomoku';
@Input() game = new Gomoku().moveTo(
'H8',
'I8',
'H9',
'H7',
'J9',
'I9',
'I10',
'H11',
'J11',
'K12',
'J10',
'J12',
'G10',
'H10',
'G8',
'F7',
'G9',
'G7',
'I7',
'J6',
'H12'
);
@Input() highlights = new Highlights();
@Input() showTools = true;
@HostListener('window:keydown.H', ['$event.target'])
@HostListener('window:keydown.J', ['$event.target'])
back() {
this.game.back();
}
@HostListener('window:keydown.T', ['$event.target'])
@HostListener('window:keydown.K', ['$event.target'])
forward() {
this.game.forward();
}
constructor() {}
log() {
console.log(this.game.getHistory());
console.log(this.game.getHistory().length);
console.log(this.highlights.highlights);
}
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/board/board.module.ts
================================================
import { NgModule } from '@angular/core';
import { BoardComponent } from './board.component';
import { CommonModule } from '@angular/common';
import { ToolsComponent } from '../tools/tools.component';
@NgModule({
imports: [CommonModule],
declarations: [BoardComponent, ToolsComponent],
exports: [BoardComponent]
})
export class GomokuBoardModule {}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/gomoku.component.css
================================================
#intro,
#outro {
/* background: url(./browser/landscape-with-trees-1.jpg) no-repeat; */
background-size: 100% auto;
display: inline-block;
height: 100vh;
}
input[type='checkbox'] {
transform: scale(2);
}
.hb,
cake {
width: 100vw;
height: 200px;
background-position: center center;
background-color: white;
background-image: url('print/hb.jpg');
background-size: 300px;
background-repeat: no-repeat;
}
kirjs-board {
width: 500px;
height: 500px;
}
:host ::ng-deep [title][blue] .slide {
background: #0079ff;
}
:host ::ng-deep [title] .slide {
align-items: center;
background: #ff6e00;
color: white;
display: flex;
height: 100vh;
justify-content: center;
text-align: center;
width: 100%;
}
h1 {
font-size: 100px;
}
tr:first-child td {
background: #ff6e00;
}
td,
th {
font-size: 40px;
padding: 20px;
}
b {
background: transparent;
color: black;
text-decoration: underline dotted;
}
a {
color: #444;
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/gomoku.component.html
================================================
Plan
What's gomoku?
Rules
Why Gomoku?
Gomoku and Computers
Sample game
Gomoku
Abstract strategy board game
coming from ancient asia (China & Japan)
tic-tac-toe
Gomoku
Rules
Rule 1: Black puts a stone first
Rule 2: Players alternate, putting a stone of their color on an empty cell
Rule 3: The first player to get 5 in a row wins
8 Reasons to play Gomoku
1. Brain massage
Lots of memorization
Patterns recognition
Problem solving
A lot of gomoku playes became successful at poker
2. Transferable life skills
Learn to win/lose
Opportunities to learn from mistakes
The skill is directly proportional to the amount of work put in
Strategical/Tacting thinking
Understanding consequences / thinking ahead
3. Social Aspect
Cross cultures/ages/accessible to everyone
Play people from different countries
Play in Tournaments or online
Play solo or in a team.
Hopefully soon play in real life :)
4. Quantifiable
All stats are out there
Learning curve - plateaus and accelerations.
5. Easy to start
Can play with pen and paper
Play online
A bunch of android apps
Very simple rules
6.Not very popular in the US
Very few people are playing
Not much has been done
Not much learning materials/tutorials/videos in english
7. Lots of space for technical solutions
No good database of games
Existing online playing platforms not perfect
8. It's Fun!
Gomoku and Computers 🤖
Complexity
Game
Space/State as log to base 10
Game Tree as log to base 10
Branching Factor
Gomoku
105
70
210
Chess
47
123
35
Go
170
360
250
Draughts (8x8)
20
20
2.8
Tic-Tac-Toe
3
5
4
More on wikipedia
In the initial rules ⚫️ always started in the center
In 1994 Victor Ellis proved that this way ⚫️ always win.
📄
Since then more advanced opening rules have been used, which haven't yet
been solved.
One of them is swap2
(Using divide and choose)
Black is stronger
A draw?
White surewin
Swap2 opening hasn't been solved by a computer
Gomocup is a yearly gomoku AI
tournament.
Now let's look at a real game
Very Advanced - Defend from 4s
How to start playing
================================================
FILE: apps/kirjs/src/app/modules/gomoku/gomoku.component.ts
================================================
import { Component } from '@angular/core';
import { parse } from 'babylon';
import { TicTacToe, Gomoku } from 'gomoku-tools';
import utils from 'gomoku-tools/src/tools/utils';
declare const require;
const json = require('./renlib/moves.json');
class Node {
p: string;
down: boolean;
depth = 0;
parent: Node;
children: Node[] = [];
constructor(public position: [number, number]) {
this.p = position.join(',');
}
addChild(node: Node) {
this.children.push(node);
node.parent = this;
node.depth = this.depth + 1;
}
}
let i = 0;
function buildTree(moves, index, parentNode) {
i++;
const move = moves[index];
let node = new Node(move.move);
node.down = !!move.down;
parentNode.addChild(node);
if (index + 1 < moves.length) {
if (move.down) {
node.down = true;
}
if (move.right) {
while (node && !node.down) {
if (!node) {
throw new Error('Weird');
}
node = node.parent;
}
node = node.parent;
}
return buildTree(moves, index + 1, node);
}
}
const parent = new Node([2, 2]);
buildTree(json.moves, 0, parent);
console.log(i);
class RenlibGame {
private current: Node;
parent;
constructor(private start) {
this.current = start;
}
back() {
if (this.current.parent) {
this.current = this.current.parent;
}
}
moveTo(point) {
let child = this.current.children.find(
({ position }) => position[0] === point[0] && position[1] === point[1]
);
if (!child) {
child = new Node(point);
this.current.children.push(child);
}
this.current = child;
}
forward() {}
getPosition() {
let node = this.current;
const game = [node.position];
while ((node = node.parent)) {
game.push(node.position);
}
const putStones = (stones, move, index) => {
stones[move[0]][move[1]] = (index % 2) + 1;
return stones;
};
const position = game
.reverse()
.reduce(putStones, utils.generateEmptyPosition(15, 15));
this.current.children
.map(n => n.position)
.reduce((stones, move) => {
stones[move[0]][move[1]] = (game.length % 2) + 1 + 2;
return stones;
}, position);
console.log(position);
return position;
}
}
@Component({
selector: 'kirjs-gomoku',
templateUrl: './gomoku.component.html',
styleUrls: ['./gomoku.component.css']
})
export class GomokuComponent {
fontSize = 18;
games = {
renlib: new RenlibGame(parent),
ticTacToe: new TicTacToe().moveTo('B2', 'A2', 'A1', 'C3', 'B1', 'B3', 'C1'),
empty: new Gomoku().moveTo(),
start: new Gomoku().moveTo('H8'),
start2: new Gomoku().moveTo('H8', 'H7'),
swap2: new Gomoku().moveTo('H8', 'E7', 'G8'), // Sure win
swap23: new Gomoku().moveTo('H8', 'E7', 'F10'), // Draw
swap24: new Gomoku().moveTo('H8', 'H13', 'M10'), // White surewin H11, 10 horizontal
swap25: new Gomoku().moveTo('C3', 'L7', 'F8'), // White surewin
swap26: new Gomoku().moveTo('H8', 'J8', 'M8'), // Central Draw
start5: new Gomoku()
.moveTo('H8', 'H7', 'I8', 'I7', 'J8', 'J7', 'K8', 'K7', 'L8')
.jumpToMove(2),
sample: new Gomoku()
.moveTo('H8', 'I7', 'H7', 'H6', 'G8', 'I8', 'H9', 'I6')
.jumpToMove(2),
start52: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J8',
'K8',
'J9',
'J7',
'G8',
'F8',
'G7',
'K7',
'L7',
'I6',
'L9',
'K5',
'K9',
'H5',
'G4',
'K4',
'K6',
'J5',
'L3',
'I5',
'L5',
'G5'
),
fork33: new Gomoku().moveTo('H8', 'H7', 'I8', 'I7', 'J7', 'I6', 'J6', 'H6'),
fork43: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J7',
'I6',
'J6',
'H6',
'G8',
'F8'
),
fork44: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J7',
'I6',
'J6',
'H6',
'G8',
'F8',
'J5',
'J4'
),
wonGame: new Gomoku().moveTo(
'H8',
'I10',
'I11',
'H10',
'J10',
'I9',
'J8',
'J9',
'K9',
'L8',
'G9',
'I7',
'I6',
'I8',
'G8',
'L7',
'K8',
'L9',
'L10',
'J7',
'K7',
'L5',
'L6',
'K6',
'M4',
'H9'
),
openThree: new Gomoku().moveTo('H8', 'H7', 'I8', 'I7', 'J8'),
old: new Gomoku().moveTo(
'h8',
'i8',
'h7',
'h9',
'j7',
'i7',
'i6',
'j5',
'h5',
'g4',
'h6',
'h4',
'f6',
'g6',
'g7',
'e5',
'f8',
'e9',
'f9',
'f7',
'g8',
'e10',
'j10',
'i9',
'j9',
'i10',
'i11',
'j8',
'e8',
'd8',
'h11',
'g10',
'h10',
'j12',
'k9',
'f11',
'e12',
'l8',
'k11',
'k8',
'm8',
'g11',
'h12',
'g13',
'g12',
'f12',
'e11',
'f4',
'i4',
'c7',
'f10',
'd6',
'g9',
'g3'
),
moreFours: new Gomoku().moveTo(
'C14',
'C12',
'D14',
'D12',
'E14',
'F12',
'G14',
'G12',
'C10',
'B10',
'E10',
'O1',
'F10',
'N1',
'G10',
'H10'
),
openThreeSpaced: new Gomoku().moveTo('H8', 'H7', 'I8', 'I7', 'K8'),
closedFour: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J8',
'K8',
'J9',
'J7',
'G8'
),
closedBrokenFour: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J8',
'K8',
'J9',
'J7',
'F8'
),
findWin: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J8',
'K8',
'J9',
'J7',
'G8',
'F8',
'G7',
'K7',
'L7',
'I6',
'L9'
),
defendFrom4s: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J8',
'K8',
'J9',
'J7',
'G8',
'F8',
'G7',
'K7',
'L7',
'I6',
'L9',
'K5',
'K6',
'H5',
'G4',
'I5',
'J5',
'I4',
'I3'
),
defendFrom4sSolved: new Gomoku().moveTo(
'H8',
'H7',
'I8',
'I7',
'J8',
'K8',
'J9',
'J7',
'G8',
'F8',
'G7',
'K7',
'L7',
'I6',
'L9',
'K5',
'K6',
'H5',
'G4',
'I5',
'J5',
'I4',
'I3',
'J6',
'N9',
'M8',
'K9',
'M9',
'L8',
'H4',
'G3',
'H6',
'H3',
'G6',
'F6'
),
many4s: new Gomoku()
.moveTo(
'h8',
'i9',
'j9',
'j8',
'h11',
'h10',
'g11',
'i11',
'i10',
'g12',
'g9',
'f10',
'g8',
'g10',
'e10',
'f9',
'f7',
'h9',
'f11',
'e8',
'd7',
'e6',
'g5',
'g7',
'e5',
'f4',
'd8',
'd9',
'd5',
'c5',
'h5',
'f5',
'f3',
'g4',
'h4',
'h3',
'i2',
'i3',
'j3',
'j5',
'k4',
'l5',
'e7',
'f6',
'b7',
'c7',
'e4',
'c6',
'g2',
'h1',
'i4',
'g6',
'd6',
'd4',
'h6',
'h7',
'k2',
'l1',
'j2',
'h2',
'l2',
'm2',
'j4',
'l4',
'k3'
)
.jumpToMove(39)
};
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/gomoku.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { BrowserWindowModule } from '@codelab/browser';
import { GomokuComponent } from './gomoku.component';
import { GomokuBoardModule } from './board/board.module';
const routes = RouterModule.forChild(SlidesRoutes.get(GomokuComponent));
@NgModule({
imports: [
routes,
BrowserWindowModule,
FeedbackModule,
CommonModule,
GomokuBoardModule,
SlidesModule
],
declarations: [GomokuComponent],
exports: [GomokuComponent]
})
export class GomokuModule {}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/highlights.spec.ts
================================================
import { Highlights } from './highlights';
describe('highlights', () => {
beforeEach(() => {
this.highlights = new Highlights();
});
it('gets empty value if there are no highlightss set', () => {
expect(this.highlights.get([0, 0])).toBe('');
});
it('Toggles single value', () => {
this.highlights.toggle([1, 2], 'pirojok');
expect(this.highlights.get([1, 2])).toBe('pirojok');
expect(this.highlights.get([5, 5])).toBe('');
this.highlights.toggle([1, 2], 'pirojok');
expect(this.highlights.get([1, 2])).toBe('');
});
it('Toggles multiple values', () => {
this.highlights.toggle([1, 2], 'a');
this.highlights.toggle([1, 2], 'b');
expect(this.highlights.get([1, 2])).toBe('a b');
this.highlights.toggle([1, 2], 'c');
expect(this.highlights.get([1, 2])).toBe('a b c');
this.highlights.toggle([1, 2], 'b');
expect(this.highlights.get([1, 2])).toBe('a c');
});
it('Allows chaning', () => {
expect(this.highlights.toggle([1, 2], 'a').get([1, 2])).toBe('a');
});
});
================================================
FILE: apps/kirjs/src/app/modules/gomoku/highlights.ts
================================================
export class Highlights {
history = [];
historyIndex = 0;
constructor(public highlights = []) {}
redo() {
if (this.historyIndex < this.history.length) {
this.historyIndex++;
const change = this.history[this.historyIndex];
this.highlights[change.point[0]][change.point[1]] = change.newValue;
}
}
undo() {
if (this.historyIndex > 0) {
this.historyIndex--;
const change = this.history[this.historyIndex];
this.highlights[change.point[0]][change.point[1]] = change.oldValue;
}
}
toggle([x, y], type) {
this.highlights[x] = this.highlights[x] || [];
this.highlights[x][y] = this.highlights[x][y] || [];
const oldValue = [...this.highlights[x][y]];
let newValue;
if (this.highlights[x][y].includes(type)) {
newValue = this.highlights[x][y] = this.highlights[x][y].filter(
a => a !== type
);
} else {
this.highlights[x][y].push(type);
newValue = this.highlights[x][y];
}
this.history = this.history.slice(0, this.historyIndex);
this.history.push({
point: [x, y],
oldValue,
newValue
});
this.historyIndex++;
return this;
}
get([x, y]) {
return (
(this.highlights[x] &&
this.highlights[x][y] &&
this.highlights[x][y].join(' ')) ||
''
);
}
clear(point?) {
const [x, y] = point;
if (point) {
this.highlights[x] = this.highlights[x] || [];
this.highlights[x][y] = [];
} else {
this.highlights = [];
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/renlib/moves.json
================================================
{
"header": { "open": 255, "type": "RenLib", "major": 3, "minor": 0 },
"moves": [
{
"move": [7, 7],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [6, 7],
"down": 1,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [5, 7],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [4, 7],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [3, 7],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [2, 7],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [1, 7],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [0, 7],
"down": 0,
"right": 1,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [6, 8],
"down": 1,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [5, 9],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [4, 8],
"down": 1,
"right": 1,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [4, 10],
"down": 0,
"right": 0,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [3, 11],
"down": 0,
"right": 1,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
},
{
"move": [7, 8],
"down": 0,
"right": 1,
"hz4": 0,
"mark": 0,
"hasComment": 0,
"hz2": 0,
"hz1": 0,
"extension": 0,
"comment": {}
}
]
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/renlib/parse.js
================================================
// Module import
const fs = require('fs');
const Parser = require('binary-parser').Parser;
const readComment = Parser.start()
.array('comment', {
type: 'uint8',
readUntil: function(a, b) {
return b.readUInt16LE() === 0 || b.length < 3;
},
formatter: function(a) {
return a
.map(v => String.fromCharCode(parseInt(v, 10).toString(10)))
.join('');
}
})
.skip(2);
const readMove = Parser.start()
.uint8('move', {
formatter: flag => [(flag % 16) - 1, Math.ceil(flag / 16) - 1]
})
.bit1('down') // 128 Has Siblings
.bit1('right') // 64 Has a child node, into the subtree
.bit1('hz4')
.bit1('mark')
.bit1('hasComment')
.bit1('hz2')
.bit1('hz1')
.bit1('extension')
.choice('comment', {
tag: 'hasComment',
choices: {
0: Parser.start(),
1: readComment
}
});
const header = Parser.start()
.endianess('little')
.uint8('open', { assert: 255 })
.string('type', {
length: 6,
assert: 'RenLib'
})
.uint8('open', { assert: 255 })
.int8('major', { assert: 3 })
.int8('minor')
.skip(10);
p = new Parser()
.nest('header', {
type: header
})
.array('moves', {
type: readMove,
readUntil: 'eof'
});
require('fs').readFile('ss.lib', function(err, data) {
const v = p.parse(data);
console.log(JSON.stringify(v));
fs.writeFileSync('moves.json', JSON.stringify(v), 'UTF-8');
});
================================================
FILE: apps/kirjs/src/app/modules/gomoku/tools/tools.component.css
================================================
.tools {
display: flex;
}
.tool.selected {
outline: 1px #888 solid;
background: #eeeeee;
}
.tool {
outline: 1px #ddd solid;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.tool-next:before {
content: '?';
}
.tool-clear:before {
content: 'X';
}
.tool-undo:before {
content: '⎌';
}
.tool-redo {
content: '⎌';
transform: scale(-1, 1);
}
.tool-red-maybe:before {
content: '🔴';
opacity: 0.5;
}
.tool-highlight:before {
content: '▩';
}
.tool-black-maybe:before {
content: '⚫';
opacity: 0.5;
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku/tools/tools.component.html
================================================
{{ selectedTool | json }}?
================================================
FILE: apps/kirjs/src/app/modules/gomoku/tools/tools.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ToolsComponent } from './tools.component';
describe('ToolsComponent', () => {
let component: ToolsComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ToolsComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ToolsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/gomoku/tools/tools.component.ts
================================================
import { Component, HostListener, Input, OnInit } from '@angular/core';
import { Highlights } from '../highlights';
@Component({
selector: 'kirjs-tools',
templateUrl: './tools.component.html',
styleUrls: ['./tools.component.css']
})
export class ToolsComponent implements OnInit {
@Input() game;
@Input() highlights;
tools = [
{
name: 'next',
action: function(point, game) {
game.moveTo(point);
},
init: function() {
return this;
}
},
{
name: 'red-maybe',
action: function(point, game, highlights) {
highlights.toggle(point, 'highlight-transparent cell-2');
},
init: function() {
return this;
}
},
{
name: 'black-maybe',
action: function(point, game, highlights) {
highlights.toggle(point, 'highlight-transparent cell-1');
},
init: function() {
return this;
}
},
{
name: 'highlight',
action: function(point, game, highlights) {
highlights.toggle(point, 'highlight-yellow');
},
init: function() {
return this;
}
},
{
name: 'highlight2',
action: function(point, game, highlights: Highlights) {
highlights.toggle(point, 'highlight-orange');
},
init: function() {
return this;
}
},
{
name: 'clear',
action: function(point, game, highlights) {
highlights.clear(point);
},
init: function() {
return this;
}
},
{
name: 'clear-all',
init: function(game, highlights: Highlights) {
highlights.clear();
}
},
{
name: 'undo',
init: function(game, highlights: Highlights) {
highlights.undo();
}
},
{
name: 'redo',
init: function(game, highlights: Highlights) {
highlights.redo();
}
}
];
selectedTool: any;
@HostListener('window:keydown', ['$event.keyCode'])
shortcut(a: number) {
if (a >= 48 && a <= 57) {
this.handle(this.tools[a - 49]);
}
}
handle(tool) {
const selected = tool.init(this.game, this.highlights);
if (selected) {
this.selectedTool = tool;
}
}
constructor() {
this.selectedTool = this.tools[0];
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/gomoku-print.component.css
================================================
#intro,
#outro {
/* background: url(./browser/landscape-with-trees-1.jpg) no-repeat; */
background-size: 100% auto;
display: inline-block;
}
h1 {
font-size: 20px;
}
h2 {
font-size: 20px;
}
.hb {
width: 200px;
height: 200px;
background: url('hb.jpg');
background-size: 200px;
background-repeat: no-repeat;
}
.header p {
font-size: 14px;
}
b {
background: #fff;
color: #000;
font-size: 14px;
font-weight: bold;
}
li {
font-size: 12px;
}
@media print {
@page {
size: A4 landscape;
}
}
.front {
display: flex;
width: 1024px;
height: 768px;
}
.main-board {
width: 360px;
height: 360px;
display: block;
margin: 0 auto;
}
.left,
.right {
width: 512px;
box-sizing: border-box;
padding: 20px;
}
.board84 {
display: block;
width: 120px;
height: 60px;
}
.explanation {
display: flex;
margin-bottom: 20px;
}
.explanation .text p {
text-align: right;
padding-right: 20px;
font-size: 12px;
margin-bottom: 2px;
}
.explanation .text {
flex: 1;
font-size: 12px;
}
.back {
margin: 0 auto;
width: 1024px;
padding: 0 60px;
display: flex;
flex-wrap: wrap;
}
.mini-board {
display: block;
width: 400px;
height: 400px;
margin-right: 30px;
margin-bottom: 20px;
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/gomoku-print.component.html
================================================
Some gomoku hints:
When playing try to create one of the patterns below:
’s have an “Open 3” attack.
if ’s don’t block it from either side, they lose.
This is also an “Open 3” .
Now there are 3 places where it can (and should) be blocked.
Here ’s have “Open 3”, but X’s went with “Open 4” attack
It’s even stronger and must be blocked asap!
And here has a “3 by 4 fork”. They will win this game!!
’s can’t block both attacks, and will lose.
(despite having an “Open 3” attack)
Enjoy!
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/gomoku-print.component.ts
================================================
import { Component } from '@angular/core';
import { parse } from 'babylon';
import { TicTacToe, Gomoku } from 'gomoku-tools';
declare const require;
@Component({
selector: 'kirjs-gomoku',
templateUrl: './gomoku-print.component.html',
styleUrls: ['./gomoku-print.component.css']
})
export class GomokuPrintComponent {
fontSize = 18;
game = new Gomoku().moveTo();
examples = {
open3: new Gomoku({ cellsX: 8, cellsY: 4 }).moveTo(
'C3',
'C2',
'D3',
'D2',
'E3'
),
open32: new Gomoku({ cellsX: 8, cellsY: 4 }).moveTo(
'C3',
'C2',
'D3',
'D2',
'F3'
),
close4: new Gomoku({ cellsX: 8, cellsY: 4 }).moveTo(
'C3',
'C2',
'D3',
'D2',
'F3',
'E2',
'E3',
'B3'
),
fork: new Gomoku({ cellsX: 8, cellsY: 5 }).moveTo(
'C3',
'C2',
'D3',
'D2',
'F3',
'E2',
'E3',
'B3',
'F4',
'E4',
'F2'
)
};
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/gomoku-print.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { GomokuPrintComponent } from './gomoku-print.component';
import { GomokuBoardModule } from '../gomoku/board/board.module';
import { XComponent } from './x/x.component';
import { OComponent } from './o/o.component';
@NgModule({
imports: [
RouterModule.forChild([{ path: `**`, component: GomokuPrintComponent }]),
GomokuBoardModule
],
declarations: [GomokuPrintComponent, XComponent, OComponent],
exports: [GomokuPrintComponent]
})
export class GomokuPrintModule {}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/o/o.component.css
================================================
:host {
color: #444;
font-size: 18px;
font-weight: bold;
line-height: 14px;
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/o/o.component.html
================================================
o
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/o/o.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OComponent } from './o.component';
describe('OComponent', () => {
let component: OComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [OComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/o/o.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
// tslint:disable-next-line
selector: 'o',
templateUrl: './o.component.html',
styleUrls: ['./o.component.css']
})
export class OComponent {}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/x/x.component.css
================================================
:host {
color: #c53a00;
font-size: 18px;
font-weight: bold;
line-height: 14px;
vertical-align: middle;
}
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/x/x.component.html
================================================
×
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/x/x.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { XComponent } from './x.component';
describe('XComponent', () => {
let component: XComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [XComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(XComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/gomoku-print/x/x.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
// tslint:disable-next-line
selector: 'x',
templateUrl: './x.component.html',
styleUrls: ['./x.component.css']
})
export class XComponent {}
================================================
FILE: apps/kirjs/src/app/modules/home/home.component.css
================================================
.wrapper {
font-family: 'Helvetica Neue', sans-serif;
font-weight: 300;
border-top: 10vw black solid;
border-bottom: 10vw black solid;
box-sizing: border-box;
}
kirjs-polaroid {
display: block;
margin-left: 30px;
}
.picture.fb {
background: url(pics/fb.png);
background-size: cover;
}
.picture.lastfm {
background: url(pics/lastfm.png);
background-size: cover;
}
.picture.binary {
background: url(pics/binary.png);
background-size: cover;
}
.picture.kirjs {
background: url(pics/kirjs.jpg);
background-size: cover;
}
a {
text-decoration: none;
color: #444;
}
================================================
FILE: apps/kirjs/src/app/modules/home/home.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/home/home.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { HomeComponent } from './home.component';
describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [HomeComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/home/home.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/home/home.module.spec.ts
================================================
import { HomeModule } from './home.module';
describe('HomeModule', () => {
let homeModule: HomeModule;
beforeEach(() => {
homeModule = new HomeModule();
});
it('should create an instance', () => {
expect(homeModule).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/home/home.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeComponent } from './home.component';
import { RouterModule } from '@angular/router';
import { PolaroidComponent } from './polaroid/polaroid.component';
@NgModule({
imports: [
CommonModule,
RouterModule.forChild([
{
path: '',
component: HomeComponent
}
])
],
declarations: [HomeComponent, PolaroidComponent],
entryComponents: [HomeComponent]
})
export class HomeModule {}
================================================
FILE: apps/kirjs/src/app/modules/home/polaroid/polaroid.component.css
================================================
::ng-deep .picture {
width: 200px;
height: 200px;
background-size: cover;
box-shadow: 0 0 10px #444 inset;
border-radius: 50%;
}
.frame-outer {
width: 240px;
height: 400px;
background: #fff;
padding: 20px;
box-sizing: border-box;
}
.text {
margin-top: 20px;
background: white;
padding: 20px;
font-size: 1.3vw;
}
================================================
FILE: apps/kirjs/src/app/modules/home/polaroid/polaroid.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/home/polaroid/polaroid.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PolaroidComponent } from './polaroid.component';
describe('PolaroidComponent', () => {
let component: PolaroidComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PolaroidComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PolaroidComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/home/polaroid/polaroid.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-polaroid',
templateUrl: './polaroid.component.html',
styleUrls: ['./polaroid.component.css']
})
export class PolaroidComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/msk/msk.component.css
================================================
.pic {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: contain;
}
.awesome {
background-image: url(./pics/awesome.png);
}
.default {
background-image: url(./pics/default.png);
}
.ivy {
background-image: url(./pics/ivy.png);
}
.differential-loading {
background-image: url(./pics/differential-loading.png);
}
================================================
FILE: apps/kirjs/src/app/modules/msk/msk.component.html
================================================
What's new in the Angular World
by @kirjs
ng deploy
Now you can deploy your angular-cli app to multiple cloud providers using
ng deploy command
ng add @angular/fire@next
ng deploy
Now you can deploy
Firebase
Netlify
Azure
Github
And more...
Differential loading
✨ Juan's tip ✨
https://browserl.ist/
cdk-experimental clipboard service + directive
================================================
FILE: apps/kirjs/src/app/modules/msk/msk.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MskComponent } from './msk.component';
describe('MskComponent', () => {
let component: MskComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MskComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MskComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/msk/msk.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-msk',
templateUrl: './msk.component.html',
styleUrls: ['./msk.component.css']
})
export class MskComponent implements OnInit {
polls = [
{
key: 'favorite',
type: 'choice',
question: 'Which framework do you use most at work?',
options: [
'Angular',
'AngularJS',
'React',
'Vue',
'Svelte',
'jQuery',
'Something else'
]
},
{
key: 'skill',
type: 'choice',
question: 'How well do you know angular?',
options: [
'Not at all',
'Somewhat',
'I can use it',
'Good',
'Really good',
"I'm Minko Fluin"
]
},
{
key: 'build-dev',
type: 'choice',
question:
'how long does it take to rebuild your app in dev mode (and see the result via local dev server) - the total turnaround time',
options: [
'< 1 second',
'1 - 5 seconds',
'5 - 10 seconds',
'10 - 30 seconds',
'30 - 60 seconds',
'1-10 minutes',
'More than 10 minutes'
]
},
{
key: 'build-prod',
type: 'choice',
question:
'how long does it take to create a production build of your app',
options: [
'< 1 second',
'1 - 5 seconds',
'5 - 10 seconds',
'10 - 30 seconds',
'30 - 60 seconds',
'1-10 minutes',
'More than 10 minutes'
]
},
{
key: 'cli',
type: 'choice',
question: 'Which feature is NOT in CLI 8.3.0-next.2 ',
answer: 'New command ng make-this-awesome',
options: [
'Redesigned default app',
'New command ng make-this-awesome',
'Faster builds with enabled differential loading',
'New command ng deploy'
]
},
{
key: 'tomorrow',
type: 'choice',
question: 'What is being released today?',
answer: 'CLI 9.0.0-next.0 with Ivy by default',
options: [
'CLI 9.0.0-next.0 with Ivy by default',
'RxJS 8',
'React 16.12',
'Angular XS'
]
},
{
key: 'material',
type: 'choice',
question:
'Which feature was added to Angular CDK library 8.1.3 "gelatin-key" (2019-08-14)?',
answer: '',
options: [
'Windows 95 theme support',
'Drag and drop',
'New material-fox component',
'New Clipboard service + directive'
]
}
];
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/msk/msk.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { RouterModule } from '@angular/router';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { SyncDataService } from '@codelab/utils/src/lib/sync/services/sync-data.service';
import { SyncSessionService } from '@codelab/utils/src/lib/sync/services/sync-session.service';
import { SyncDbService } from '@codelab/utils/src/lib/sync/services/sync-db.service';
import { SyncPollService } from '@codelab/utils/src/lib/sync/components/poll/common/sync-poll.service';
import { SyncRegistrationService } from '@codelab/utils/src/lib/sync/components/registration/sync-registration.service';
import { SyncButtonModule } from '@codelab/utils/src/lib/sync/sync-button/sync-button.module';
import { QuestionsModule } from '@codelab/utils/src/lib/sync/components/questions/questions.module';
import { SyncModule } from '@codelab/utils/src/lib/sync/sync.module';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { SyncDirectivesModule } from '@codelab/utils/src/lib/sync/directives/sync-directives.module';
import { SyncRegistrationModule } from '@codelab/utils/src/lib/sync/components/registration/sync-registration.module';
import { SyncPollModule } from '@codelab/utils/src/lib/sync/components/poll/sync-poll.module';
import { MskComponent } from './msk.component';
const routes = RouterModule.forChild(SlidesRoutes.get(MskComponent));
@NgModule({
providers: [
SyncDataService,
SyncSessionService,
SyncDbService,
SyncPollService,
SyncRegistrationService
],
declarations: [MskComponent],
imports: [
CommonModule,
SlidesModule,
routes,
QuestionsModule,
SyncModule,
AngularFireAuthModule,
AngularFireDatabaseModule,
SyncButtonModule,
SyncDirectivesModule,
SyncRegistrationModule,
SyncPollModule
]
})
export class MskModule {}
================================================
FILE: apps/kirjs/src/app/modules/music/music.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/music/music.component.html
================================================
music works!
================================================
FILE: apps/kirjs/src/app/modules/music/music.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MusicComponent } from './music.component';
describe('MusicComponent', () => {
let component: MusicComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MusicComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MusicComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/music/music.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-music',
templateUrl: './music.component.html',
styleUrls: ['./music.component.css']
})
export class MusicComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/music/music.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MusicComponent } from './music.component';
@NgModule({
imports: [CommonModule],
declarations: [MusicComponent]
})
export class MusicModule {}
================================================
FILE: apps/kirjs/src/app/modules/qna/qna.component.css
================================================
:host ::ng-deep {
display: block;
padding: 40px;
position: relative;
}
================================================
FILE: apps/kirjs/src/app/modules/qna/qna.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/qna/qna.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { QnaComponent } from './qna.component';
describe('QnaComponent', () => {
let component: QnaComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [QnaComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(QnaComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/qna/qna.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-qna',
templateUrl: './qna.component.html',
styleUrls: ['./qna.component.css']
})
export class QnaComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/qna/qna.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CommonModule } from '@angular/common';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFireDatabaseModule } from '@angular/fire/database';
import { SlidesModule } from '@ng360/slides';
import { QuestionsModule } from '@codelab/utils/src/lib/sync/components/questions/questions.module';
import { SyncModule } from '@codelab/utils/src/lib/sync/sync.module';
import { SyncDataService } from '@codelab/utils/src/lib/sync/services/sync-data.service';
import { SyncRegistrationService } from '@codelab/utils/src/lib/sync/components/registration/sync-registration.service';
import { SyncSessionService } from '@codelab/utils/src/lib/sync/services/sync-session.service';
import { SyncDbService } from '@codelab/utils/src/lib/sync/services/sync-db.service';
import { SyncPollService } from '@codelab/utils/src/lib/sync/components/poll/common/sync-poll.service';
import { SyncButtonModule } from '@codelab/utils/src/lib/sync/sync-button/sync-button.module';
import { SyncDirectivesModule } from '@codelab/utils/src/lib/sync/directives/sync-directives.module';
import { SyncRegistrationModule } from '@codelab/utils/src/lib/sync/components/registration/sync-registration.module';
import { QnaComponent } from './qna.component';
const routes = RouterModule.forChild([{ path: '', component: QnaComponent }]);
@NgModule({
declarations: [QnaComponent],
providers: [
SyncDataService,
SyncSessionService,
SyncDbService,
SyncPollService,
SyncRegistrationService
],
imports: [
CommonModule,
SlidesModule,
routes,
QuestionsModule,
SyncModule,
AngularFireAuthModule,
AngularFireDatabaseModule,
SyncButtonModule,
SyncDirectivesModule,
SyncRegistrationModule
]
})
export class QnaModule {}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/index.ts
================================================
export * from './live.module';
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live-mock/index.ts
================================================
export * from './live-mock.module';
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live-mock/live-mock.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live-mock/live-mock.component.html
================================================
Presenter
Viewer
{{ data | json }}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live-mock/live-mock.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LiveMockComponent } from './live-mock.component';
describe('LiveMockComponent', () => {
let component: LiveMockComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LiveMockComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LiveMockComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live-mock/live-mock.component.ts
================================================
import { Component, OnDestroy, OnInit } from '@angular/core';
import { LiveService, LiveInfo } from '../live.service';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { FormBuilder, FormGroup } from '@angular/forms';
@Component({
selector: 'kirjs-live-mock-component',
templateUrl: './live-mock.component.html',
styleUrls: ['./live-mock.component.css']
})
export class LiveMockComponent implements OnInit, OnDestroy {
data: LiveInfo;
form: FormGroup = this.fb.group({
user: this.fb.control(''),
status: this.fb.control('')
});
private onDestroy: Subject = new Subject();
constructor(private service: LiveService, private fb: FormBuilder) {}
ngOnInit() {
this.form.valueChanges.subscribe(data => {
this.service.storeLiveInfo(data);
});
this.service.liveInfo.pipe(takeUntil(this.onDestroy)).subscribe(data => {
this.data = data;
this.form.patchValue(data, { emitEvent: false });
});
}
ngOnDestroy(): void {
this.onDestroy.next(null);
this.onDestroy.complete();
}
}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live-mock/live-mock.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { LiveMockComponent } from './live-mock.component';
@NgModule({
imports: [CommonModule, ReactiveFormsModule],
declarations: [LiveMockComponent],
exports: [LiveMockComponent]
})
export class LiveMockModule {}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live.module.ts
================================================
import { NgModule } from '@angular/core';
import { LiveMockModule } from './live-mock';
import { PollModule } from './poll';
@NgModule({
exports: [LiveMockModule, PollModule]
})
export class LiveModule {}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live.service.spec.ts
================================================
import { TestBed } from '@angular/core/testing';
import { LiveService } from './live.service';
describe('LiveService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: LiveService = TestBed.inject(LiveService);
expect(service).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/regex/live/live.service.ts
================================================
import { Injectable, OnDestroy } from '@angular/core';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
export interface LiveInfo {
// Login service
user: string;
// Global
sessionId: string;
//
status: 'presenter' | 'viewer'; // in the future 'admin';
// e.g. regex
presentationId: string;
// slide id
slide: string;
}
type AllData = Record;
@Injectable({
providedIn: 'root'
})
export class LiveService implements OnDestroy {
private liveInfoSubject: BehaviorSubject = new BehaviorSubject<
LiveInfo
>({
user: 'code',
sessionId: 'test',
status: 'presenter', // 'viewer'
presentationId: 'regex',
slide: 'config'
} as LiveInfo);
liveInfo: Observable = this.liveInfoSubject.asObservable();
private allDataSubject: BehaviorSubject> = new BehaviorSubject<
AllData
>({} as AllData);
allData = this.allDataSubject.asObservable();
myData = combineLatest([this.allData, this.liveInfo]).pipe(
map(([allData, { user }]) => {
return allData[user];
})
);
constructor() {}
storeLiveInfo(data: LiveInfo): void {
const liveInfo = this.liveInfoSubject.getValue();
this.liveInfoSubject.next({ ...liveInfo, ...data });
}
// Viewer
storeMyData(data: T): void {
// firebase.store
const liveInfo = this.liveInfoSubject.getValue();
const allData = this.allDataSubject.getValue();
const value = { ...allData, [liveInfo.user]: data };
this.allDataSubject.next(value);
}
ngOnDestroy() {
if (this.allDataSubject) {
this.allDataSubject.complete();
this.allDataSubject = null;
}
if (this.liveInfoSubject) {
this.liveInfoSubject.complete();
this.liveInfoSubject = null;
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/poll/index.ts
================================================
export * from './poll.module';
================================================
FILE: apps/kirjs/src/app/modules/regex/live/poll/poll.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/regex/live/poll/poll.component.html
================================================
2
{{ service.allData | async | json }} 3
Results are here!!!
// results myDAta
================================================
FILE: apps/kirjs/src/app/modules/regex/live/poll/poll.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PollComponent } from './poll.component';
describe('SyncPollComponent', () => {
let component: PollComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PollComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PollComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/regex/live/poll/poll.component.ts
================================================
import { Component, Injectable, Input, OnInit } from '@angular/core';
import { LiveService } from '../live.service';
@Component({
selector: 'kirjs-poll',
templateUrl: './poll.component.html',
styleUrls: ['./poll.component.css']
})
export class PollComponent {
@Input() question: string;
constructor(readonly service: LiveService) {}
}
@Component({
selector: 'kirjs-poll-answer',
template: `
`,
styleUrls: ['./poll.component.css']
})
export class SlidesAnswerComponent implements OnInit {
@Input() value: string;
constructor(readonly service: LiveService) {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/regex/live/poll/poll.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { PollComponent, SlidesAnswerComponent } from './poll.component';
@NgModule({
declarations: [PollComponent, SlidesAnswerComponent],
exports: [PollComponent, SlidesAnswerComponent],
imports: [CommonModule]
})
export class PollModule {}
================================================
FILE: apps/kirjs/src/app/modules/regex/regex.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/regex/regex.component.html
================================================
Hello
Yes
No
================================================
FILE: apps/kirjs/src/app/modules/regex/regex.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RegexComponent } from './regex.component';
describe('RegexComponent', () => {
let component: RegexComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [RegexComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RegexComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/regex/regex.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-regex',
templateUrl: './regex.component.html',
styleUrls: ['./regex.component.css']
})
export class RegexComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/regex/regex.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { RegexComponent } from './regex.component';
import { LiveModule } from './live';
const routes = RouterModule.forChild(SlidesRoutes.get(RegexComponent));
@NgModule({
declarations: [RegexComponent],
imports: [routes, CommonModule, SlidesModule, LiveModule]
})
export class RegexModule {}
================================================
FILE: apps/kirjs/src/app/modules/stack/simple-stack/simple-stack.component.css
================================================
:host {
white-space: nowrap;
border: 3px #666 dotted;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
border-right: 0;
padding-left: 10px;
padding-right: 40px;
height: 46px;
position: relative;
}
:host:after {
content: '';
position: absolute;
left: 0;
top: 0;
height: 46px;
right: 80px;
background: white;
opacity: 0.7;
}
================================================
FILE: apps/kirjs/src/app/modules/stack/simple-stack/simple-stack.component.html
================================================
{{ value }}
================================================
FILE: apps/kirjs/src/app/modules/stack/simple-stack/simple-stack.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SimpleStackComponent } from './simple-stack.component';
describe('SimpleStackComponent', () => {
let component: SimpleStackComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SimpleStackComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SimpleStackComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/stack/simple-stack/simple-stack.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'slides-simple-stack',
templateUrl: './simple-stack.component.html',
styleUrls: ['./simple-stack.component.css']
})
export class SimpleStackComponent implements OnInit {
@Input() value: string;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function/stack-function.component.css
================================================
:host {
white-space: nowrap;
}
.grid {
display: grid;
grid-template-columns: 1fr 80px 1fr;
}
.arrow {
text-align: center;
}
:host.disabled {
opacity: 0.4;
background: #eee;
}
.inputs {
justify-self: end;
white-space: nowrap;
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function/stack-function.component.html
================================================
{{ func.name }}
({{ func.inputs }})
=>
{{ func.outputs }}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function/stack-function.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StackFunctionComponent } from './stack-function.component';
describe('StackFunctionComponent', () => {
let component: StackFunctionComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StackFunctionComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StackFunctionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function/stack-function.component.ts
================================================
import { Component, HostBinding, Input, OnInit } from '@angular/core';
import { StackFunction } from '../stack-game.component';
@Component({
selector: 'slides-stack-function',
templateUrl: './stack-function.component.html',
styleUrls: ['./stack-function.component.css']
})
export class StackFunctionComponent implements OnInit {
@Input() func: StackFunction;
@HostBinding('class.disabled')
@Input()
disabled = false;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function-button/stack-function-button.component.css
================================================
button {
font-size: inherit;
cursor: pointer;
background: #ffffff;
border: 1px #ddd solid;
border-radius: 10px;
padding: 10px;
margin: 0 10px;
}
button:hover {
background: #eee;
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function-button/stack-function-button.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function-button/stack-function-button.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StackFunctionButtonComponent } from './stack-function-button.component';
describe('StackFunctionButtonComponent', () => {
let component: StackFunctionButtonComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StackFunctionButtonComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StackFunctionButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-function-button/stack-function-button.component.ts
================================================
import { Component, HostBinding, Input, OnInit } from '@angular/core';
import { StackFunction } from '../stack-game.component';
@Component({
// tslint:disable-next-line:component-selector
selector: 'slides-stack-function-button',
templateUrl: './stack-function-button.component.html',
styleUrls: ['./stack-function-button.component.css']
})
export class StackFunctionButtonComponent {
@Input() func: StackFunction;
@Input() disabled = false;
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-game.component.css
================================================
.buttons {
margin: 20px 0 40px;
text-align: center;
}
.complete {
text-align: center;
}
.stack {
margin-left: 30px;
}
.item {
white-space: nowrap;
margin-bottom: 10px;
height: 55px;
display: block;
line-height: 55px;
}
.initial {
color: #666;
text-align: right;
}
.function-usage {
text-align: right;
}
.history {
display: grid;
grid-template-columns: 1fr 1fr;
}
.next {
background: #dddddd;
text-align: center;
border-radius: 5px;
}
.remove-button {
margin-right: 10px;
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-game.component.html
================================================
✨💖 Success! ✨💖
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-game.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StackGameComponent } from './stack-game.component';
describe('StackGameComponent', () => {
let component: StackGameComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StackGameComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StackGameComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-game/stack-game.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
export interface StackFunction {
inputs: string;
outputs: string;
name?: string;
}
export interface Level {
functions: StackFunction[];
inputs: string;
outputs: string;
}
const ANY_CHAR = '*';
@Component({
selector: 'slides-stack-game',
templateUrl: './stack-game.component.html',
styleUrls: ['./stack-game.component.css']
})
export class StackGameComponent implements OnInit {
isComplete = false;
@Input() level: Level = {
functions: [
{
inputs: '',
outputs: '🍏',
name: 'push 🍏'
},
{
inputs: '🍏🍏',
outputs: '🍋'
},
{
inputs: '🍋🍋',
outputs: '🍒'
},
{
inputs: '*',
outputs: '',
name: 'pop'
}
],
inputs: '🍏',
outputs: '🍒'
};
functions = [];
stack = '';
history: string[];
canAddFunction(stack: string, func) {
return stack.match(new RegExp(func.inputs.replace(ANY_CHAR, '.') + '$'));
}
calcStack() {
let stack = this.level.inputs.replace(ANY_CHAR, '🍏');
const history = [];
for (const func of this.functions) {
stack =
stack.slice(
0,
stack.length - func.inputs.replace(ANY_CHAR, '🍏').length
) + func.outputs;
history.push(stack);
}
this.history = history;
this.stack = stack;
if (this.stack === this.level.outputs) {
this.isComplete = true;
}
}
addFunction(func: StackFunction) {
this.functions.push(func);
this.calcStack();
}
removeFunction() {
this.functions.pop();
this.calcStack();
}
ngOnInit() {
this.stack = this.level.inputs;
}
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-routing.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SlidesRoutes } from '@ng360/slides';
import { StackComponent } from './stack.component';
import { StackModule } from './stack.module';
const routes = RouterModule.forChild(SlidesRoutes.get(StackComponent));
@NgModule({
imports: [StackModule, routes]
})
export class StackRoutingModule {}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-test/stack-test.component.html
================================================
Test time! those two companies have "stack" in their name, but only one of
them actually has stack as their logo. which one?
StackOverflow.com
StackBlitz.com
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-test/stack-test.component.scss
================================================
.wrapper {
display: flex;
.company {
text-align: center;
flex: 1;
.name {
font-size: 40px;
font-weight: 300;
margin: 20px 0;
}
.logo {
height: 150px;
> div {
background-size: cover;
margin: 20px auto;
}
.sb {
width: 80px;
height: 120px;
background-image: url('./assets/sb-icon.svg');
}
.so {
width: 150px;
height: 150px;
background-image: url('./assets/so-icon.svg');
}
}
}
.gap {
flex: 0.2;
}
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-test/stack-test.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StackTestComponent } from './stack-test.component';
describe('StackTestComponent', () => {
let component: StackTestComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StackTestComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StackTestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/stack/stack-test/stack-test.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: 'slides-stack-test',
templateUrl: './stack-test.component.html',
styleUrls: ['./stack-test.component.scss']
})
export class StackTestComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack.component.css
================================================
:host ::ng-deep .slide.slide > div {
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
padding: 0 20vw;
}
::ng-deep {
font-weight: 300;
}
.button-wrapper {
margin: 20px 0;
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack.component.html
================================================
Stack is a data structure, serving as a collection. Here's a stack
of fruit:
All operations are possible only with the last element of the stack.
First operation is called pop , it removes the last element.
The other operation is push . It adds an element on top of the
stack.
Some implementations also have a peek operation. It tells you what
top element on the stack is. You can try it below:
Congrats! It is a 🍋
Let's practice!
Use the commands you know already to get a stack of:
Congrats! Now you are a stack expert!
But why are stacks important? And how can I use them in a language like
asm
Let's learn stack machine.
This is one of the fundamental principles used in JVM Java Byte Code,
WebAssembly byte code
Stack machine offers various intructions that can pop and then push
multiple element on the stack
For example (🌲🍏)=>🍍 takes two elements from the stack (Pine and Apple)
and pushes a 🍍 back
Now use the instructions below to prepare a lemonade drink:
================================================
FILE: apps/kirjs/src/app/modules/stack/stack.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StackComponent } from './stack.component';
describe('StackComponent', () => {
let component: StackComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StackComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StackComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/stack/stack.component.ts
================================================
import { Component, OnInit } from '@angular/core';
import { Level } from './stack-game/stack-game.component';
@Component({
selector: 'kirjs-stack',
templateUrl: './stack.component.html',
styleUrls: ['./stack.component.css']
})
export class StackComponent implements OnInit {
itIsALemon = false;
levels: Record = {
push: {
functions: [
{
inputs: '',
outputs: '🍏',
name: 'push 🍏'
},
{
inputs: '',
outputs: '🍋',
name: 'push 🍋'
}
],
inputs: '',
outputs: '🍏🍋🍏'
},
pop: {
functions: [
{
inputs: '*',
outputs: '',
name: 'pop'
}
],
inputs: '🍏🍏🍏🍏🍏',
outputs: '🍏'
},
together: {
functions: [
{
inputs: '*',
outputs: '',
name: 'pop'
},
{
inputs: '',
outputs: '🍓',
name: 'push 🍓'
},
{
inputs: '',
outputs: '🍋',
name: 'push 🍋'
}
],
inputs: '🍏🍏',
outputs: '🍓🍋'
},
lemonade: {
functions: [
{
inputs: '',
outputs: '💦'
},
{
inputs: '',
outputs: '🍋'
},
{
inputs: '',
outputs: '🍒'
},
{
inputs: '🍒💦🍋',
outputs: '🍹'
}
],
inputs: '',
outputs: '🍹'
},
level1: {
functions: [
{
inputs: '',
outputs: '🍏🍏'
},
{
inputs: '',
outputs: '🍋'
},
{
inputs: '🍋🍋',
outputs: '🍒'
},
{
inputs: '*',
outputs: '',
name: 'pop'
}
],
inputs: '🍏',
outputs: '🍒'
},
level2: {
functions: [
{
inputs: '',
outputs: '🍏',
name: 'push 🍏'
},
{
inputs: '🍏🍏',
outputs: '🍋'
},
{
inputs: '🍋🍋',
outputs: '🍒'
},
{
inputs: '*',
outputs: '',
name: 'pop'
}
],
inputs: '🍏',
outputs: '🍒'
}
};
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/stack/stack.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SlidesModule } from '@ng360/slides';
import { StackComponent } from './stack.component';
import { StackGameComponent } from './stack-game/stack-game.component';
import { SimpleStackComponent } from './simple-stack/simple-stack.component';
import { StackTestComponent } from './stack-test/stack-test.component';
import { StackFunctionComponent } from './stack-game/stack-function/stack-function.component';
import { StackFunctionButtonComponent } from './stack-game/stack-function-button/stack-function-button.component';
import { MatButtonModule } from '@angular/material/button';
@NgModule({
declarations: [
StackComponent,
StackGameComponent,
SimpleStackComponent,
StackTestComponent,
StackFunctionComponent,
StackFunctionButtonComponent
],
exports: [
StackComponent,
StackGameComponent,
SimpleStackComponent,
StackTestComponent,
StackFunctionComponent,
StackFunctionButtonComponent
],
imports: [CommonModule, SlidesModule, MatButtonModule]
})
export class StackModule {}
================================================
FILE: apps/kirjs/src/app/modules/streaming/common.ts
================================================
import { InjectionToken } from '@angular/core';
export const FLAME_LINK = new InjectionToken('FLAME_LINK');
interface Guest {
name: string;
twitter: string;
avatar: string;
}
export interface StreamSession {
name: string;
guests: Guest[];
}
================================================
FILE: apps/kirjs/src/app/modules/streaming/overlay/overlay.component.html
================================================
{{ config.header }}
{{ config.subHeader }}
{{ guest.name }}
@{{ guest.twitter }}
Chat
@kirjs
@{{ guest.twitter }}
================================================
FILE: apps/kirjs/src/app/modules/streaming/overlay/overlay.component.scss
================================================
:host {
border: 1px #ddd solid;
width: 1650px;
height: 1050px;
display: block;
font-family: 'Helvetica Neue', sans-serif;
position: relative;
}
.circle {
position: absolute;
right: 20px;
bottom: 20px;
height: 240px;
width: 240px;
border-radius: 50%;
}
:host ::ng-deep {
h2 {
margin-bottom: 0;
font-family: 'Helvetica Neue', sans-serif;
font-weight: 300;
}
p {
margin-top: 8px;
}
}
================================================
FILE: apps/kirjs/src/app/modules/streaming/overlay/overlay.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OverlayComponent } from './overlay.component';
describe('OverlayComponent', () => {
let component: OverlayComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [OverlayComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OverlayComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/streaming/overlay/overlay.component.ts
================================================
import { Component, Inject, OnInit } from '@angular/core';
import { FLAME_LINK, StreamSession } from '../common';
import { interval, Observable } from 'rxjs';
import { map, startWith, switchMap } from 'rxjs/operators';
@Component({
selector: 'slides-overlay',
templateUrl: './overlay.component.html',
styleUrls: ['./overlay.component.scss']
})
export class OverlayComponent implements OnInit {
readonly layout = 'horizontal';
data$: Observable = interval(5000).pipe(
startWith(0),
switchMap(() => {
return this.flameLink.content.get({
schemaKey: 'currentSession',
populate: true
});
}),
map((a: any) => a.session)
);
constructor(@Inject(FLAME_LINK) private flameLink: any) {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/streaming/streaming.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { OverlayComponent } from './overlay/overlay.component';
import firebase from 'firebase/app';
// Add additional services that you want to use
import 'firebase/auth';
import 'firebase/firestore';
import flamelink from 'flamelink/app';
// Add additional modules that you want to use
import 'flamelink/content';
import 'flamelink/storage';
import { FLAME_LINK } from './common';
import { MarkdownModule } from 'ngx-markdown';
const firebaseConfig = {
apiKey: 'AIzaSyC_Zyq9Ve1SrbenuN0iDlDd4hQvTIlruP8',
authDomain: 'kirjs-c884f.firebaseapp.com',
databaseURL: 'https://kirjs-c884f.firebaseio.com',
projectId: 'kirjs-c884f',
storageBucket: 'kirjs-c884f.appspot.com',
messagingSenderId: '651206687896',
appId: '1:651206687896:web:3df45fa9e636bb5882a4ed',
measurementId: 'G-3B7YEC4QG7'
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
const app = flamelink({
firebaseApp,
env: 'production', // optional, defaults to `production`
locale: 'en-US', // optional, defaults to `en-US`
dbType: 'cf' // optional, defaults to `rtdb` - can be 'rtdb' or 'cf' (Realtime DB vs Cloud Firestore)
});
@NgModule({
declarations: [],
providers: [
{
provide: FLAME_LINK,
useValue: app
}
],
imports: [
RouterModule.forChild([
{
path: '',
component: OverlayComponent
}
]),
CommonModule,
MarkdownModule
]
})
export class StreamingModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/app.component.ts
================================================
import { Component } from '@angular/core';
// Just an empty component to make everything compile
@Component({
selector: 'kirjs-app',
template: ''
})
export class AppComponent {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/attr/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'kirjs-app',
template: `
`
})
export class AppComponent {
y = 200;
constructor() {
window.setInterval(() => {
this.y = Math.random() * 300;
}, 200);
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/bs.module.ts
================================================
import { AppComponent as A1 } from './sub.component';
import { AppComponent as A2 } from './attr/app.component';
import { AppComponent as A3 } from './chart/app.component';
import { AppComponent as A4 } from './svg/app.component';
import { AppComponent as A5 } from './chart4/app.component.solved';
import { AppComponent as A6 } from './chart2/app.component.solved';
import { Component, Input, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'kirjs-ticks',
template: ''
})
export class FakeTicksComponent {
@Input() data: any;
}
@NgModule({
imports: [CommonModule],
declarations: [A1, A2, A3, A4, A5, A6, FakeTicksComponent]
})
export class AppModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart/app.component.ts
================================================
import { Component } from '@angular/core';
function generateData() {
return Array.from(new Array(10)).map(index =>
Math.round(Math.random() * 300)
);
}
@Component({
selector: 'kirjs-app',
template: `
{{ item }}
`
})
export class AppComponent {
barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
data = generateData();
constructor() {
window.setInterval(() => {
this.data = generateData();
}, 1000);
}
getIndex(a, b) {
return a;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart2/app.component.solved.ts
================================================
import { Component } from '@angular/core';
function generateData() {
return Array.from(new Array(10)).map(index => ({
index,
value: Math.round(Math.random() * 300)
}));
}
@Component({
selector: 'kirjs-app',
template: `
{{ item.value }}
`
})
export class AppComponent {
barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
data = generateData();
constructor() {
window.setInterval(() => {
this.data = generateData();
}, 1000);
}
getIndex(a, b) {
return a;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart2/app.component.ts
================================================
import { Component } from '@angular/core';
function generateData() {
return Array.from(new Array(10)).map(index => ({
index,
value: Math.round(Math.random() * 300)
}));
}
@Component({
selector: 'kirjs-app',
template: `
{{ item.value }}
`
})
export class AppComponent {
barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
data = generateData();
constructor() {
window.setInterval(() => {
this.data = generateData();
}, 1000);
}
getIndex(a, b) {
return a;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart2/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { TicksComponent } from './ticks.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, TicksComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart2/ticks.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'kirjs-ticks',
template: `
{{ i }}
`
})
export class TicksComponent {
@Input() data;
@Input() barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
getIndex(i: number) {
return i;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart3/app.component.ts
================================================
import { Component } from '@angular/core';
function generateData() {
return Array.from(new Array(10)).map(index => ({
index,
value: Math.round(Math.random() * 300)
}));
}
@Component({
selector: 'kirjs-app',
template: `
{{ item.value }}
`
})
export class AppComponent {
barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
data = generateData();
constructor() {
window.setInterval(() => {
this.data = generateData();
}, 1000);
}
getIndex(a, b) {
return a;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart3/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { TicksComponent } from './ticks.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, TicksComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart3/ticks.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'kirjs-ticks',
template: `
{{ i }}
`
})
export class TicksComponent {
@Input() data;
@Input() barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
getIndex(i: number) {
return i;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart4/app.component.solved.ts
================================================
import { Component } from '@angular/core';
function generateData() {
return Array.from(new Array(10)).map(index => ({
index,
value: Math.round(Math.random() * 300)
}));
}
@Component({
selector: 'kirjs-app',
template: `
{{ item.value }}
`
})
export class AppComponent {
barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
data = generateData();
constructor() {
window.setInterval(() => {
this.data = generateData();
}, 1000);
}
getIndex(a, b) {
return a;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart4/app.component.ts
================================================
import { Component } from '@angular/core';
function generateData() {
return Array.from(new Array(10)).map(index => ({
index,
value: Math.round(Math.random() * 300)
}));
}
@Component({
selector: 'kirjs-app',
template: `
{{ item.value }}
`
})
export class AppComponent {
barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
data = generateData();
constructor() {
window.setInterval(() => {
this.data = generateData();
}, 1000);
}
getIndex(a, b) {
return a;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart4/app.module.ts
================================================
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { TicksComponent } from './ticks.component';
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent, TicksComponent],
bootstrap: [AppComponent]
})
export class AppModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/chart4/ticks.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
selector: 'kirjs-ticks',
template: `
{{ i }}
`
})
export class TicksComponent {
@Input() data;
@Input() barWidth = 30;
padding = 10;
barSpace = this.padding + this.barWidth;
getIndex(i: number) {
return i;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/index.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/style.css
================================================
svg,
body,
html {
width: 100%;
height: 100%;
}
text {
font-family: sans-serif;
text-anchor: middle;
}
rect,
text,
g {
transition: 1s;
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/sub.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'kirjs-app',
template: `
`
})
export class AppComponent {
y = 200;
constructor() {
window.setInterval(() => {
this.y = Math.random() * 300;
}, 200);
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/samples/svg/app.component.ts
================================================
import { Component } from '@angular/core';
@Component({
selector: 'kirjs-app',
template: `
`
})
export class AppComponent {
y = 200;
constructor() {
window.setInterval(() => {
this.y = Math.random() * 300;
}, 200);
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-demo/svg-demo.component.css
================================================
:host {
height: 100%;
display: block;
}
:host ::ng-deep svg {
width: 100%;
height: 100%;
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-demo/svg-demo.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-demo/svg-demo.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SvgDemoComponent } from './svg-demo.component';
describe('SvgDemoComponent', () => {
let component: SvgDemoComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SvgDemoComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SvgDemoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-demo/svg-demo.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-svg-demo',
templateUrl: './svg-demo.component.html',
styleUrls: ['./svg-demo.component.css']
})
export class SvgDemoComponent implements OnInit {
code: string;
@Input() fontSize;
@Input('code')
set codeInput(value) {
this.code = '\n' + value + '\n ';
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-playground/svg-playground.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-playground/svg-playground.component.html
================================================
svg-playground works!
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-playground/svg-playground.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SvgPlaygroundComponent } from './svg-playground.component';
describe('RaceComponent', () => {
let component: SvgPlaygroundComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SvgPlaygroundComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SvgPlaygroundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-playground/svg-playground.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-svg-playground',
templateUrl: './svg-playground.component.html',
styleUrls: ['./svg-playground.component.css']
})
export class SvgPlaygroundComponent implements OnInit {
@Input() code: string;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together/svg-together.component.css
================================================
:host {
height: 100%;
display: block;
}
:host ::ng-deep svg {
width: 100%;
height: 100%;
}
code-demo-editor {
height: 500px;
width: 100%;
display: inline-block;
border: 1px solid #000;
}
::ng-deep svg {
width: 500px;
height: 500px;
border: 1px solid #999999;
min-height: 500px;
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together/svg-together.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together/svg-together.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SvgTogetherComponent } from './svg-together.component';
describe('SvgTogetherComponent', () => {
let component: SvgTogetherComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SvgTogetherComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SvgTogetherComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together/svg-together.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
@Component({
selector: 'kirjs-svg-together',
templateUrl: './svg-together.component.html',
styleUrls: ['./svg-together.component.css']
})
export class SvgTogetherComponent implements OnInit {
code = '';
angularFireList: AngularFireList;
@Input() fontSize;
allCode = 'TBD';
helpers = [
{
label: '⭕️',
code: `
`
},
{
label: '⬭',
code: `
`
},
{
label: '▭',
code: `
`
},
{
label: '_',
code: `
`
},
{
label: 't',
code: `
LOL❤
`
},
{
label: '☆',
code: `
`
},
{
label: '⌇',
code: `
`
}
];
constructor(af: AngularFireDatabase) {
this.angularFireList = af.list('/svg-together');
this.angularFireList.snapshotChanges().subscribe(a => {
this.allCode =
'' + a.map(a => a.payload.val()).join('\n') + ' ';
});
this.reset();
}
@Input('code')
set codeInput(value) {
this.code = '\n' + value + '\n ';
}
submit() {
const code = this.code.replace(/\s+/, '').replace(' ', '');
this.angularFireList.push(code);
this.reset();
}
reset() {
this.code = `
`;
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together-result/svg-together-result.component.css
================================================
:host {
height: 100%;
display: block;
}
:host ::ng-deep svg {
width: 100%;
height: 100%;
}
code-demo-editor {
height: 500px;
width: 100%;
display: inline-block;
border: 1px solid #000;
}
::ng-deep svg {
width: 500px;
height: 500px;
border: 1px solid #999999;
min-height: 500px;
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together-result/svg-together-result.component.html
================================================
https://codelab.fun/svg/draw
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together-result/svg-together-result.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SvgTogetherResultComponent } from './svg-together-result.component';
describe('SvgTogetherResultComponent', () => {
let component: SvgTogetherResultComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SvgTogetherResultComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SvgTogetherResultComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg/svg-together-result/svg-together-result.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
@Component({
selector: 'kirjs-svg-together-result',
templateUrl: './svg-together-result.component.html',
styleUrls: ['./svg-together-result.component.css']
})
export class SvgTogetherResultComponent implements OnInit {
code = '';
angularFireList: AngularFireList;
@Input() fontSize;
allCode = 'TBD';
constructor(af: AngularFireDatabase) {
this.angularFireList = af.list('/svg-together');
this.angularFireList.snapshotChanges().subscribe(a => {
this.allCode =
'' +
a.map(a => a.payload.val()).join('\n') +
' ';
});
}
@Input('code')
set codeInput(value) {
this.code = '\n' + value + '\n ';
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg.component.css
================================================
:host ::ng-deep .monaco-editor .view-overlays .current-line {
display: none;
}
:host ::ng-deep .decorationsOverviewRuler {
display: none;
}
:host ::ng-deep .scrollbar.vertical {
display: none;
}
.timer {
position: fixed;
left: 20px;
bottom: 20px;
}
.bg {
width: 100%;
height: 100%;
background-repeat: no-repeat;
background-size: cover;
}
.intro {
background-image: url(pics/1.jpg);
}
.kirjs {
background-image: url(pics/ava.gif);
}
.svg-60 {
background-image: url(pics/2.jpg);
}
.example {
background-image: url(pics/3.jpg);
}
.the-end {
background-image: url(pics/4.jpg);
}
.dog {
background-image: url(pics/5.gif);
}
.bg.intro h2 {
background: #000;
color: white;
margin-top: 50vh;
padding: 50px;
font-size: 10vh;
opacity: 0.7;
}
.bg.kirjs h2 {
background: #000;
color: white;
margin-top: 50vh;
padding: 10px;
font-size: 10vh;
opacity: 0.7;
}
.bg h2 {
background: #fff;
color: black;
margin-top: 50vh;
padding: 50px;
font-size: 10vh;
opacity: 0.7;
}
.btn-bar {
line-height: 3vw;
}
.btn-bar:hover .font-size {
display: block;
font-size: 4vw;
}
.btn-bar .font-size {
display: none;
}
.twitter {
color: #444;
font-size: 3vw;
margin: 2vw;
}
================================================
FILE: apps/kirjs/src/app/modules/svg/svg.component.html
================================================
What is SVG?
Scalable Vector Graphic Format
Can be edited with a text editor!
Every object is just an XML tag
Works with CSS
Plays well with angular templates!
Some cool stuff
Now you're an SVG expert!
However SVG has a couple of edge cases
Sign up for my 80 hour workshop "Wait, SVG, LOL, Really?"
Let's check out some examples:
SVG attributes binding
Let's create a simple barchart
Custom SVG Elements
================================================
FILE: apps/kirjs/src/app/modules/svg/svg.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { SvgComponent } from './svg.component';
import { FormsModule } from '@angular/forms';
import { SvgDemoComponent } from './svg-demo/svg-demo.component';
import { SvgPlaygroundComponent } from './svg-playground/svg-playground.component';
import { TimerComponent } from './timer/timer.component';
import { CommonModule } from '@angular/common';
import { SvgTogetherComponent } from './svg-together/svg-together.component';
import { MatButtonModule } from '@angular/material/button';
import { SharedPipeModule } from '@codelab/utils/src/lib/pipes/pipes.module';
import { SlidesModule } from '@ng360/slides';
import { CodeDemoModule } from '@codelab/code-demos';
import { SvgTogetherResultComponent } from './svg-together-result/svg-together-result.component';
import { NewProgressBarModule } from '../ast/new-progress-bar/new-progress-bar.module';
const routes = RouterModule.forChild(SlidesRoutes.get(SvgComponent));
@NgModule({
imports: [
routes,
CommonModule,
FeedbackModule,
FormsModule,
MatButtonModule,
NewProgressBarModule,
SharedPipeModule,
SlidesModule,
CodeDemoModule
],
declarations: [
SvgComponent,
SvgTogetherComponent,
SvgTogetherResultComponent,
SvgDemoComponent,
SvgPlaygroundComponent,
TimerComponent
],
exports: [SvgComponent]
})
export class SvgModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg/timer/timer.component.css
================================================
.timer-running {
color: #444;
font-size: 3vw;
}
================================================
FILE: apps/kirjs/src/app/modules/svg/timer/timer.component.html
================================================
▶️
0" class="timer-running">{{ time }}️
================================================
FILE: apps/kirjs/src/app/modules/svg/timer/timer.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TimerComponent } from './timer.component';
describe('TimerComponent', () => {
let component: TimerComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TimerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TimerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg/timer/timer.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-timer',
templateUrl: './timer.component.html',
styleUrls: ['./timer.component.css']
})
export class TimerComponent implements OnInit {
time = 0;
reset() {
this.time = 180;
}
constructor() {
window.setInterval(() => {
if (this.time > 0) {
this.time--;
}
}, 1000);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/finish/finish.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/finish/finish.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/finish/finish.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FinishComponent } from './finish.component';
describe('FinishComponent', () => {
let component: FinishComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FinishComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FinishComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg-race/finish/finish.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: '[kirjs-finish]',
templateUrl: './finish.component.html',
styleUrls: ['./finish.component.css']
})
export class FinishComponent implements OnInit {
@Input() position = { x: 0, y: 0 };
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/little-car/little-car.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/little-car/little-car.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/little-car/little-car.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LittleCarComponent } from './little-car.component';
describe('LittleCarComponent', () => {
let component: LittleCarComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LittleCarComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LittleCarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg-race/little-car/little-car.component.ts
================================================
import { Component, Input } from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: '[kirjs-little-car]',
templateUrl: './little-car.component.html',
styleUrls: ['./little-car.component.css']
})
export class LittleCarComponent {
@Input() position = { x: 0, y: 0, angle: 0 };
lightColor = '#ffbc05';
darkColor = '#e38100';
@Input()
set color(color: string) {
this.lightColor = color;
this.darkColor = '#444';
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/player/player.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/player/player.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/player/player.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { PlayerComponent } from './player.component';
describe('PlayerComponent', () => {
let component: PlayerComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [PlayerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(PlayerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg-race/player/player.component.ts
================================================
import {
AfterViewInit,
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild
} from '@angular/core';
@Component({
// tslint:disable-next-line:component-selector
selector: '[kirjs-player]',
templateUrl: './player.component.html',
styleUrls: ['./player.component.css']
})
export class PlayerComponent implements OnInit, AfterViewInit, OnChanges {
@ViewChild('guess', { static: true }) guess: ElementRef;
@Input() path;
@Input() d = '';
@Input() color = '#ffffff';
@Output() scoreChanged = new EventEmitter();
points = [];
carPosition = { x: 50, y: 50, angle: 0 };
pathLength = 0;
startPosition = { x: 0, y: 0 };
trackWidth = 20;
score = 0;
ngOnChanges(changes: SimpleChanges) {
if (changes.d) {
this.calculateScore();
}
}
calculateScore() {
requestAnimationFrame(() => {
this.calculateCarPosition();
this.score = 0;
const guess = this.guess.nativeElement;
for (let i = 0; i < this.points.length; i++) {
const point = this.points[i];
const { distance } = closestPoint(guess, point);
if (distance < this.trackWidth) {
this.score++;
} else {
this.pathLength = ((i - 3) / 100) * this.path.getTotalLength();
this.scoreChanged.emit(this.score);
return;
}
}
this.scoreChanged.emit(this.score);
});
}
ngAfterViewInit() {
requestAnimationFrame(() => {
const path = this.path;
const l = path.getTotalLength();
for (let i = 0; i < l; i += l / 100) {
this.points.push(path.getPointAtLength(i));
}
this.startPosition = path.getPointAtLength(0);
});
}
calculateCarPosition() {
const guess = this.guess.nativeElement;
const totalLength = guess.getTotalLength();
this.carPosition = guess.getPointAtLength(totalLength);
if (totalLength > 1) {
const carPosition = guess.getPointAtLength(totalLength - 1);
const dx = this.carPosition.x - carPosition.x;
const dy = this.carPosition.y - carPosition.y;
this.carPosition.angle = (Math.atan2(-dx, dy) * 180) / Math.PI;
}
}
ngOnInit() {}
}
function closestPoint(pathNode, point) {
const pathLength = pathNode.getTotalLength();
let precision = 8,
best,
bestLength,
bestDistance = Infinity;
// linear scan for coarse approximation
for (
let scan, scanLength = 0, scanDistance;
scanLength <= pathLength;
scanLength += precision
) {
if (
(scanDistance = distance2(
(scan = pathNode.getPointAtLength(scanLength))
)) < bestDistance
) {
(best = scan), (bestLength = scanLength), (bestDistance = scanDistance);
}
}
// binary search for precise estimate
precision /= 2;
while (precision > 0.5) {
let before, after, beforeLength, afterLength, beforeDistance, afterDistance;
if (
(beforeLength = bestLength - precision) >= 0 &&
(beforeDistance = distance2(
(before = pathNode.getPointAtLength(beforeLength))
)) < bestDistance
) {
(best = before),
(bestLength = beforeLength),
(bestDistance = beforeDistance);
} else if (
(afterLength = bestLength + precision) <= pathLength &&
(afterDistance = distance2(
(after = pathNode.getPointAtLength(afterLength))
)) < bestDistance
) {
(best = after),
(bestLength = afterLength),
(bestDistance = afterDistance);
} else {
precision /= 2;
}
}
best = [best.x, best.y];
best.distance = Math.sqrt(bestDistance);
return best;
function distance2(p) {
const dx = p.x - point.x,
dy = p.y - point.y;
return dx * dx + dy * dy;
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/race/race.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/race/race.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/svg-race/race/race.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RaceComponent } from './race.component';
describe('RaceComponent', () => {
let component: RaceComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [RaceComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RaceComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg-race/race/race.component.ts
================================================
import {
AfterViewInit,
Component,
ElementRef,
Input,
ViewChild
} from '@angular/core';
@Component({
selector: 'kirjs-race',
templateUrl: './race.component.html',
styleUrls: ['./race.component.css']
})
export class RaceComponent implements AfterViewInit {
@Input() code: string;
d = 'M50 450 v -5';
cars = [
{
name: 'cheburek',
color: '#fd6b00',
d: this.d,
score: 0
},
{
name: 'banana',
color: '#fde200',
d: 'M50 450 v 5 l -10 -300',
score: 0
},
{
name: 'ololo',
color: '#bdfd00',
d: 'M50 450 v -5 h 10v-300',
score: 0
}
];
@Input() track: string;
@ViewChild('path', { static: false }) path: ElementRef;
name = 'cheburek';
scores = {};
trackWidth = 20;
finishPosition = { x: 0, y: 0 };
updateCurrentPlayer() {
const c = this.cars.find(car => car.name === this.name);
if (c) {
c.d = this.d;
}
}
ngAfterViewInit() {
const path = this.path.nativeElement;
this.finishPosition = path.getPointAtLength(path.getTotalLength());
}
setScore(name: string, score: number) {
const c = this.cars.find(car => name === car.name);
if (c) {
c.score = score;
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/svg-race.component.css
================================================
:host ::ng-deep .monaco-editor .view-overlays .current-line {
display: none;
}
:host ::ng-deep .decorationsOverviewRuler {
display: none;
}
:host ::ng-deep .scrollbar.vertical {
display: none;
}
.timer {
position: fixed;
left: 20px;
bottom: 20px;
}
.btn-bar {
line-height: 3vw;
}
.btn-bar:hover .font-size {
display: block;
font-size: 4vw;
}
.btn-bar .font-size {
display: none;
}
.twitter {
color: #444;
font-size: 3vw;
margin: 2vw;
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/svg-race.component.html
================================================
Anybody will see this info!
Only presenter sees this
Only viewer sees this
You are a presenter
You are a viewer
What is SVG?
Scalable Vector Graphic Format
Every object is just an XML tag
Also works with CSS
LOL
Race
Race
================================================
FILE: apps/kirjs/src/app/modules/svg-race/svg-race.component.ts
================================================
import { Component } from '@angular/core';
import {
bootstrap,
builder,
exercise,
stylesheet
} from '../../../../../codelab/src/app/shared/helpers/helpers';
declare const require;
@Component({
selector: 'kirjs-svg-race',
templateUrl: './svg-race.component.html',
styleUrls: ['./svg-race.component.css']
})
export class SvgRaceComponent {
fontSize = 20;
tracks = {
advanced: `M50 450 Q -50 -60 300 50
Q 380 75 400 150
Q 450 350 300 150
Q 250 50 150 120
Q 50 250 150 320
Q 250 420 450 320
`
};
input = 'hi';
input2: any;
constructor() {}
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/svg-race.module.ts
================================================
import { NgModule, Pipe, PipeTransform } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DomSanitizer } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { MatButtonModule } from '@angular/material/button';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { FeedbackModule } from '@codelab/feedback';
import { SyncModule } from '@codelab/utils/src/lib/sync/sync.module';
import { CodeDemoModule } from '@codelab/code-demos';
import { TimerComponent } from './timer/timer.component';
import { RaceComponent } from './race/race.component';
import { LittleCarComponent } from './little-car/little-car.component';
import { FinishComponent } from './finish/finish.component';
import { PlayerComponent } from './player/player.component';
import { SvgRaceComponent } from './svg-race.component';
import { ButtonsNavBarModule } from '../../../../../codelab/src/app/components/buttons-nav-bar/buttons-nav-bar.module';
const routes = RouterModule.forChild(SlidesRoutes.get(SvgRaceComponent));
@Pipe({ name: 'safeHtml' })
export class SafeHtml implements PipeTransform {
constructor(private readonly sanitizer: DomSanitizer) {}
transform(html) {
return this.sanitizer.bypassSecurityTrustHtml(html);
}
}
@NgModule({
imports: [
routes,
CommonModule,
SlidesModule,
ButtonsNavBarModule,
FeedbackModule,
CodeDemoModule,
FormsModule,
MatButtonModule,
SyncModule,
ReactiveFormsModule
],
declarations: [
RaceComponent,
SafeHtml,
SvgRaceComponent,
TimerComponent,
LittleCarComponent,
FinishComponent,
PlayerComponent
],
exports: [SvgRaceComponent]
})
export class SvgRaceModule {}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/timer/timer.component.css
================================================
.timer-running {
color: #444;
font-size: 3vw;
}
================================================
FILE: apps/kirjs/src/app/modules/svg-race/timer/timer.component.html
================================================
▶️
0" class="timer-running">{{ time }}️
================================================
FILE: apps/kirjs/src/app/modules/svg-race/timer/timer.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TimerComponent } from './timer.component';
describe('TimerComponent', () => {
let component: TimerComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TimerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TimerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/svg-race/timer/timer.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-timer',
templateUrl: './timer.component.html',
styleUrls: ['./timer.component.css']
})
export class TimerComponent implements OnInit {
time = 0;
reset() {
this.time = 180;
}
constructor() {
window.setInterval(() => {
if (this.time > 0) {
this.time--;
}
}, 1000);
}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/sync/sync.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/sync/sync.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/sync/sync.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SyncComponent } from './sync.component';
describe('SyncComponent', () => {
let component: SyncComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SyncComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SyncComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/sync/sync.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-sync',
templateUrl: './sync.component.html',
styleUrls: ['./sync.component.css']
})
export class SyncComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/sync/sync.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SlidesModule, SlidesRoutes } from '@ng360/slides';
import { RouterModule } from '@angular/router';
import { SyncModule as SyncLibModule } from '@codelab/utils/src/lib/sync/sync.module';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { SyncSessionsComponent } from '@codelab/utils/src/lib/sync/components/sync-sessions/sync-sessions.component';
import { SyncComponent } from './sync.component';
const routes = RouterModule.forChild([
{ path: 'sessions', component: SyncSessionsComponent },
...SlidesRoutes.get(SyncComponent)
]);
@NgModule({
declarations: [SyncComponent],
imports: [
CommonModule,
SlidesModule,
routes,
SyncLibModule,
AngularFireAuthModule
]
})
export class SyncModule {}
================================================
FILE: apps/kirjs/src/app/modules/test/test.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/test/test.component.html
================================================
test works!
================================================
FILE: apps/kirjs/src/app/modules/test/test.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TestComponent } from './test.component';
describe('TestComponent', () => {
let component: TestComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TestComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TestComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/test/test.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'kirjs-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/test/test.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { SlidesRoutes } from '@ng360/slides';
import { TestComponent } from './test.component';
const routes = RouterModule.forChild(SlidesRoutes.get(TestComponent));
@NgModule({
imports: [CommonModule, routes],
declarations: [TestComponent],
entryComponents: [TestComponent]
})
export class TestModule {}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/ca.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SingleCellComponent } from './single-cell/single-cell.component';
import { SingleGridComponent } from './single-grid/single-grid.component';
@NgModule({
declarations: [SingleCellComponent, SingleGridComponent],
exports: [SingleCellComponent, SingleGridComponent],
imports: [CommonModule]
})
export class CaModule {}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-cell/single-cell.component.css
================================================
:host {
display: flex;
height: 100%;
width: 100%;
align-items: center;
justify-content: space-around;
}
.cell {
width: 10vh;
height: 10vh;
border: 4px #444 solid;
animation: toColor 5s infinite linear, size 5s linear;
}
@keyframes size {
0% {
width: 60vh;
height: 60vh;
}
100% {
width: 10vh;
height: 10vh;
}
}
@keyframes toColor {
0% {
background: #000;
}
45% {
background: #000;
}
50% {
background: #fff;
}
95% {
background: #fff;
}
100% {
background: #000;
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-cell/single-cell.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-cell/single-cell.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SingleCellComponent } from './single-cell.component';
describe('SingleCellComponent', () => {
let component: SingleCellComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SingleCellComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SingleCellComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-cell/single-cell.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'slides-single-cell',
templateUrl: './single-cell.component.html',
styleUrls: ['./single-cell.component.css']
})
export class SingleCellComponent implements OnInit {
@Input() single = true;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-grid/single-grid.component.css
================================================
:host {
display: flex;
height: 100%;
width: 100%;
align-items: center;
justify-content: center;
}
.cell {
width: 10vh;
height: 10vh;
animation: toColor 4s linear infinite;
/*animation: size 15s linear;*/
}
.row {
display: flex;
}
.central {
animation: none;
background: #000;
}
@keyframes size {
0% {
width: 60vh;
height: 60vh;
}
100% {
width: 10vh;
height: 10vh;
}
}
@keyframes toColor {
0% {
background: #000;
}
45% {
background: #000;
}
50% {
background: #fff;
}
95% {
background: #fff;
}
100% {
background: #000;
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-grid/single-grid.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-grid/single-grid.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SingleGridComponent } from './single-grid.component';
describe('SingleGridComponent', () => {
let component: SingleGridComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [SingleGridComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SingleGridComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/ca/single-grid/single-grid.component.ts
================================================
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'slides-single-grid',
templateUrl: './single-grid.component.html',
styleUrls: ['./single-grid.component.css']
})
export class SingleGridComponent implements OnInit {
readonly f = [...new Array(9)].map((a, i) => i);
readonly randomDelays = this.f.map(a => this.f.map(b => Math.random() * 5));
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/full-screen-runner/full-screen-runner.component.css
================================================
================================================
FILE: apps/kirjs/src/app/modules/webassembly/full-screen-runner/full-screen-runner.component.html
================================================
90, 110, 184, 30
================================================
FILE: apps/kirjs/src/app/modules/webassembly/full-screen-runner/full-screen-runner.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FullScreenRunnerComponent } from './full-screen-runner.component';
describe('FullScreenRunnerComponent', () => {
let component: FullScreenRunnerComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FullScreenRunnerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FullScreenRunnerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/full-screen-runner/full-screen-runner.component.ts
================================================
import { Component, Input, OnChanges, OnInit } from '@angular/core';
import { extractExpressionByMatch } from '../utils';
@Component({
selector: 'slides-full-screen-runner',
templateUrl: './full-screen-runner.component.html',
styleUrls: ['./full-screen-runner.component.css']
})
export class FullScreenRunnerComponent implements OnInit, OnChanges {
@Input() code: any;
cellSize = 10;
wat: string;
js: string;
width = 2000;
height = 1800;
rowSize = 100;
rule = 1;
steps = 100;
constructor() {}
ngOnInit() {}
ngOnChanges() {
this.prepare();
}
prepare() {
const expected = (256 + this.rule)
.toString(2)
.substr(1)
.split('')
.map(Number)
.reverse()
.map(n => (n ? ' $enable' : ' $disable'))
.join('\n');
this.wat = this.code.wat;
this.js = this.code.js;
this.js = this.js.replace(/size = \d+/, 'size = ' + this.cellSize);
this.js = this.js.replace(/steps: \d+/, 'steps: ' + this.steps);
this.js = this.js.replace(/rowSize: \d+/, 'rowSize: ' + this.rowSize);
const elem = extractExpressionByMatch(/\(elem/, this.wat);
const newElem = `(elem (i32.const 0)
${expected}
)`;
this.wat = this.wat.replace(elem, newElem);
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/full-screen-runner/full-screen-runner.module.ts
================================================
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FullScreenRunnerComponent } from './full-screen-runner.component';
import { WebassemblyRunnerModule } from '../webassembly-playground/webassembly-runner/webassembly-runner.module';
import { MatInputModule } from '@angular/material/input';
import { FormsModule } from '@angular/forms';
import { CellularAutomationModule } from '../../cellular-automation/cellular-automation.module';
@NgModule({
declarations: [FullScreenRunnerComponent],
exports: [FullScreenRunnerComponent],
imports: [
CommonModule,
WebassemblyRunnerModule,
MatInputModule,
FormsModule,
CellularAutomationModule
]
})
export class FullScreenRunnerModule {}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/monaco-wat.ts
================================================
/* Copyright 2018 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the 'Software'), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare const monaco;
const languages = monaco.languages;
const CompletionItemKind = languages.CompletionItemKind;
const CompletionItemInsertTextRule = languages.CompletionItemInsertTextRule;
type CompletionItem = typeof languages.CompletionItem;
type IModel = any;
type IPosition = any;
type IRichLanguageConfiguration = any;
let completionItems: CompletionItem[] = null;
export function getWatCompletionItems() {
const keyword = CompletionItemKind.Keyword;
if (completionItems) {
return completionItems;
}
return (completionItems = [
{ label: 'module', documentation: '', kind: keyword, insertText: 'module' },
{
label: 'func',
documentation: 'function declaration',
kind: keyword,
insertText: 'func'
},
{
label: 'param',
documentation: 'parameter',
kind: keyword,
insertText: { value: 'param ${1:identifier} ${2:type}' } as any
},
{
label: 'i32',
documentation: '32-bit integer',
kind: keyword,
insertText: 'i32'
},
{
label: 'i64',
documentation: '64-bit integer',
kind: keyword,
insertText: 'i64'
},
{
label: 'f32',
documentation: '32-bit floating point',
kind: keyword,
insertText: 'f32'
},
{
label: 'f64',
documentation: '64-bit floating point',
kind: keyword,
insertText: 'f64'
},
{
label: 'anyfunc',
documentation: 'function reference',
kind: keyword,
insertText: 'anyfunc'
},
{
label: 'i32.load8_s',
documentation: 'load 1 byte and sign-extend i8 to i32',
kind: keyword,
insertText: 'i32.load8_s'
},
{
label: 'i32.load8_u',
documentation: 'load 1 byte and zero-extend i8 to i32',
kind: keyword,
insertText: 'i32.load8_u'
},
{
label: 'i32.load16_s',
documentation: 'load 2 bytes and sign-extend i16 to i32',
kind: keyword,
insertText: 'i32.load16_s'
},
{
label: 'i32.load16_u',
documentation: 'load 2 bytes and zero-extend i16 to i32',
kind: keyword,
insertText: 'i32.load16_u'
},
{
label: 'i32.load',
documentation: 'load 4 bytes as i32',
kind: keyword,
insertText: 'i32.load'
},
{
label: 'i64.load8_s',
documentation: 'load 1 byte and sign-extend i8 to i64',
kind: keyword,
insertText: 'i64.load8_s'
},
{
label: 'i64.load8_u',
documentation: 'load 1 byte and zero-extend i8 to i64',
kind: keyword,
insertText: 'i64.load8_u'
},
{
label: 'i64.load16_s',
documentation: 'load 2 bytes and sign-extend i16 to i64',
kind: keyword,
insertText: 'i64.load16_s'
},
{
label: 'i64.load16_u',
documentation: 'load 2 bytes and zero-extend i16 to i64',
kind: keyword,
insertText: 'i64.load16_u'
},
{
label: 'i64.load32_s',
documentation: 'load 4 bytes and sign-extend i32 to i64',
kind: keyword,
insertText: 'i64.load32_s'
},
{
label: 'i64.load32_u',
documentation: 'load 4 bytes and zero-extend i32 to i64',
kind: keyword,
insertText: 'i64.load32_u'
},
{
label: 'i64.load',
documentation: 'load 8 bytes as i64',
kind: keyword,
insertText: 'i64.load'
},
{
label: 'f32.load',
documentation: 'load 4 bytes as f32',
kind: keyword,
insertText: 'f32.load'
},
{
label: 'f64.load',
documentation: 'load 8 bytes as f64',
kind: keyword,
insertText: 'f64.load'
},
{
label: 'i32.store8',
documentation: 'wrap i32 to i8 and store 1 byte',
kind: keyword,
insertText: 'i32.store8'
},
{
label: 'i32.store16',
documentation: 'wrap i32 to i16 and store 2 bytes',
kind: keyword,
insertText: 'i32.store16'
},
{
label: 'i32.store',
documentation: '(no conversion) store 4 bytes',
kind: keyword,
insertText: 'i32.store'
},
{
label: 'i64.store8',
documentation: 'wrap i64 to i8 and store 1 byte',
kind: keyword,
insertText: 'i64.store8'
},
{
label: 'i64.store16',
documentation: 'wrap i64 to i16 and store 2 bytes',
kind: keyword,
insertText: 'i64.store16'
},
{
label: 'i64.store32',
documentation: 'wrap i64 to i32 and store 4 bytes',
kind: keyword,
insertText: 'i64.store32'
},
{
label: 'i64.store',
documentation: '(no conversion) store 8 bytes',
kind: keyword,
insertText: 'i64.store'
},
{
label: 'f32.store',
documentation: '(no conversion) store 4 bytes',
kind: keyword,
insertText: 'f32.store'
},
{
label: 'f64.store',
documentation: '(no conversion) store 8 bytes',
kind: keyword,
insertText: 'f64.store'
},
{
label: 'get_local',
documentation: 'read the current value of a local variable',
kind: keyword,
insertText: 'get_local'
},
{
label: 'set_local',
documentation: 'set the current value of a local variable',
kind: keyword,
insertText: 'set_local'
},
{
label: 'tee_local',
documentation: 'like `set_local`, but also returns the set value',
kind: keyword,
insertText: 'tee_local'
},
{
label: 'get_global',
documentation: 'get the current value of a global variable',
kind: keyword,
insertText: 'get_global'
},
{
label: 'set_global',
documentation: 'set the current value of a global variable',
kind: keyword,
insertText: 'set_global'
},
{
label: 'nop',
documentation: 'no operation, no effect',
kind: keyword,
insertText: 'nop'
},
{
label: 'block',
documentation:
'the beginning of a block construct, a sequence of instructions with a label at the end',
kind: keyword,
insertText: 'block'
},
{
label: 'loop',
documentation:
'a block with a label at the beginning which may be used to form loops',
kind: keyword,
insertText: 'loop'
},
{
label: 'if',
documentation:
'the beginning of an if construct with an implicit *then* block',
kind: keyword,
insertText: 'if'
},
{
label: 'else',
documentation: 'marks the else block of an if',
kind: keyword,
insertText: 'else'
},
{
label: 'br',
documentation: 'branch to a given label in an enclosing construct',
kind: keyword,
insertText: 'br'
},
{
label: 'br_if',
documentation:
'conditionally branch to a given label in an enclosing construct',
kind: keyword,
insertText: 'br_if'
},
{
label: 'br_table',
documentation:
'a jump table which jumps to a label in an enclosing construct',
kind: keyword,
insertText: 'br_table'
},
{
label: 'return',
documentation: 'return zero or more values from this function',
kind: keyword,
insertText: 'return'
},
{
label: 'end',
documentation:
'an instruction that marks the end of a block, loop, if, or function',
kind: keyword,
insertText: 'end'
},
{
label: 'call',
documentation: 'call function directly',
kind: keyword,
insertText: 'call'
},
{
label: 'call_indirect',
documentation: 'call function indirectly',
kind: keyword,
insertText: 'call_indirect'
},
{
label: 'i64.const',
documentation: 'produce the value of an i64 immediate',
kind: keyword,
insertText: { value: 'i64.const ${1:constant}' }
},
{
label: 'i32.const',
documentation: 'produce the value of an i32 immediate',
kind: keyword,
insertText: { value: 'i32.const ${1:constant}' }
},
{
label: 'f32.const',
documentation: 'produce the value of an f32 immediate',
kind: keyword,
insertText: { value: 'f32.const ${1:constant}' }
},
{
label: 'f64.const',
documentation: 'produce the value of an f64 immediate',
kind: keyword,
insertText: { value: 'f64.const ${1:constant}' }
},
{
label: 'i32.add',
documentation: 'sign-agnostic addition',
kind: keyword,
insertText: 'i32.add'
},
{
label: 'i32.sub',
documentation: 'sign-agnostic subtraction',
kind: keyword,
insertText: 'i32.sub'
},
{
label: 'i32.mul',
documentation: 'sign-agnostic multiplication (lower 32-bits)',
kind: keyword,
insertText: 'i32.mul'
},
{
label: 'i32.div_s',
documentation: 'signed division (result is truncated toward zero)',
kind: keyword,
insertText: 'i32.div_s'
},
{
label: 'i32.div_u',
documentation:
'unsigned division (result is [floored](https://en.wikipedia.org/wiki/Floor_and_ceiling_functions))',
kind: keyword,
insertText: 'i32.div_u'
},
{
label: 'i32.rem_s',
documentation: 'signed remainder (result has the sign of the dividend)',
kind: keyword,
insertText: 'i32.rem_s'
},
{
label: 'i32.rem_u',
documentation: 'unsigned remainder',
kind: keyword,
insertText: 'i32.rem_u'
},
{
label: 'i32.and',
documentation: 'sign-agnostic bitwise and',
kind: keyword,
insertText: 'i32.and'
},
{
label: 'i32.or',
documentation: 'sign-agnostic bitwise inclusive or',
kind: keyword,
insertText: 'i32.or'
},
{
label: 'i32.xor',
documentation: 'sign-agnostic bitwise exclusive or',
kind: keyword,
insertText: 'i32.xor'
},
{
label: 'i32.shl',
documentation: 'sign-agnostic shift left',
kind: keyword,
insertText: 'i32.shl'
},
{
label: 'i32.shr_u',
documentation: 'zero-replicating (logical) shift right',
kind: keyword,
insertText: 'i32.shr_u'
},
{
label: 'i32.shr_s',
documentation: 'sign-replicating (arithmetic) shift right',
kind: keyword,
insertText: 'i32.shr_s'
},
{
label: 'i32.rotl',
documentation: 'sign-agnostic rotate left',
kind: keyword,
insertText: 'i32.rotl'
},
{
label: 'i32.rotr',
documentation: 'sign-agnostic rotate right',
kind: keyword,
insertText: 'i32.rotr'
},
{
label: 'i32.eq',
documentation: 'sign-agnostic compare equal',
kind: keyword,
insertText: 'i32.eq'
},
{
label: 'i32.ne',
documentation: 'sign-agnostic compare unequal',
kind: keyword,
insertText: 'i32.ne'
},
{
label: 'i32.lt_s',
documentation: 'signed less than',
kind: keyword,
insertText: 'i32.lt_s'
},
{
label: 'i32.le_s',
documentation: 'signed less than or equal',
kind: keyword,
insertText: 'i32.le_s'
},
{
label: 'i32.lt_u',
documentation: 'unsigned less than',
kind: keyword,
insertText: 'i32.lt_u'
},
{
label: 'i32.le_u',
documentation: 'unsigned less than or equal',
kind: keyword,
insertText: 'i32.le_u'
},
{
label: 'i32.gt_s',
documentation: 'signed greater than',
kind: keyword,
insertText: 'i32.gt_s'
},
{
label: 'i32.ge_s',
documentation: 'signed greater than or equal',
kind: keyword,
insertText: 'i32.ge_s'
},
{
label: 'i32.gt_u',
documentation: 'unsigned greater than',
kind: keyword,
insertText: 'i32.gt_u'
},
{
label: 'i32.ge_u',
documentation: 'unsigned greater than or equal',
kind: keyword,
insertText: 'i32.ge_u'
},
{
label: 'i32.clz',
documentation:
'sign-agnostic count leading zero bits (All zero bits are considered leading if the value is zero)',
kind: keyword,
insertText: 'i32.clz'
},
{
label: 'i32.ctz',
documentation:
'sign-agnostic count trailing zero bits (All zero bits are considered trailing if the value is zero)',
kind: keyword,
insertText: 'i32.ctz'
},
{
label: 'i32.popcnt',
documentation: 'sign-agnostic count number of one bits',
kind: keyword,
insertText: 'i32.popcnt'
},
{
label: 'i32.eqz',
documentation:
'compare equal to zero (return 1 if operand is zero, 0 otherwise)',
kind: keyword,
insertText: 'i32.eqz'
},
{
label: 'f32.add',
documentation: 'addition',
kind: keyword,
insertText: 'f32.add'
},
{
label: 'f32.sub',
documentation: 'subtraction',
kind: keyword,
insertText: 'f32.sub'
},
{
label: 'f32.mul',
documentation: 'multiplication',
kind: keyword,
insertText: 'f32.mul'
},
{
label: 'f32.div',
documentation: 'division',
kind: keyword,
insertText: 'f32.div'
},
{
label: 'f32.abs',
documentation: 'absolute value',
kind: keyword,
insertText: 'f32.abs'
},
{
label: 'f32.neg',
documentation: 'negation',
kind: keyword,
insertText: 'f32.neg'
},
{
label: 'f32.copysign',
documentation: 'copysign',
kind: keyword,
insertText: 'f32.copysign'
},
{
label: 'f32.ceil',
documentation: 'ceiling operator',
kind: keyword,
insertText: 'f32.ceil'
},
{
label: 'f32.floor',
documentation: 'floor operator',
kind: keyword,
insertText: 'f32.floor'
},
{
label: 'f32.trunc',
documentation: 'round to nearest integer towards zero',
kind: keyword,
insertText: 'f32.trunc'
},
{
label: 'f32.nearest',
documentation: 'round to nearest integer, ties to even',
kind: keyword,
insertText: 'f32.nearest'
},
{
label: 'f32.eq',
documentation: 'compare ordered and equal',
kind: keyword,
insertText: 'f32.eq'
},
{
label: 'f32.ne',
documentation: 'compare unordered or unequal',
kind: keyword,
insertText: 'f32.ne'
},
{
label: 'f32.lt',
documentation: 'compare ordered and less than',
kind: keyword,
insertText: 'f32.lt'
},
{
label: 'f32.le',
documentation: 'compare ordered and less than or equal',
kind: keyword,
insertText: 'f32.le'
},
{
label: 'f32.gt',
documentation: 'compare ordered and greater than',
kind: keyword,
insertText: 'f32.gt'
},
{
label: 'f32.ge',
documentation: 'compare ordered and greater than or equal',
kind: keyword,
insertText: 'f32.ge'
},
{
label: 'f32.sqrt',
documentation: 'square root',
kind: keyword,
insertText: 'f32.sqrt'
},
{
label: 'f32.min',
documentation:
'minimum (binary operator); if either operand is NaN, returns NaN',
kind: keyword,
insertText: 'f32.min'
},
{
label: 'f32.max',
documentation:
'maximum (binary operator); if either operand is NaN, returns NaN',
kind: keyword,
insertText: 'f32.max'
},
{
label: 'f64.add',
documentation: 'addition',
kind: keyword,
insertText: 'f64.add'
},
{
label: 'f64.sub',
documentation: 'subtraction',
kind: keyword,
insertText: 'f64.sub'
},
{
label: 'f64.mul',
documentation: 'multiplication',
kind: keyword,
insertText: 'f64.mul'
},
{
label: 'f64.div',
documentation: 'division',
kind: keyword,
insertText: 'f64.div'
},
{
label: 'f64.abs',
documentation: 'absolute value',
kind: keyword,
insertText: 'f64.abs'
},
{
label: 'f64.neg',
documentation: 'negation',
kind: keyword,
insertText: 'f64.neg'
},
{
label: 'f64.copysign',
documentation: 'copysign',
kind: keyword,
insertText: 'f64.copysign'
},
{
label: 'f64.ceil',
documentation: 'ceiling operator',
kind: keyword,
insertText: 'f64.ceil'
},
{
label: 'f64.floor',
documentation: 'floor operator',
kind: keyword,
insertText: 'f64.floor'
},
{
label: 'f64.trunc',
documentation: 'round to nearest integer towards zero',
kind: keyword,
insertText: 'f64.trunc'
},
{
label: 'f64.nearest',
documentation: 'round to nearest integer, ties to even',
kind: keyword,
insertText: 'f64.nearest'
},
{
label: 'f64.eq',
documentation: 'compare ordered and equal',
kind: keyword,
insertText: 'f64.eq'
},
{
label: 'f64.ne',
documentation: 'compare unordered or unequal',
kind: keyword,
insertText: 'f64.ne'
},
{
label: 'f64.lt',
documentation: 'compare ordered and less than',
kind: keyword,
insertText: 'f64.lt'
},
{
label: 'f64.le',
documentation: 'compare ordered and less than or equal',
kind: keyword,
insertText: 'f64.le'
},
{
label: 'f64.gt',
documentation: 'compare ordered and greater than',
kind: keyword,
insertText: 'f64.gt'
},
{
label: 'f64.ge',
documentation: 'compare ordered and greater than or equal',
kind: keyword,
insertText: 'f64.ge'
},
{
label: 'f64.sqrt',
documentation: 'square root',
kind: keyword,
insertText: 'f64.sqrt'
},
{
label: 'f64.min',
documentation:
'minimum (binary operator); if either operand is NaN, returns NaN',
kind: keyword,
insertText: 'f64.min'
},
{
label: 'f64.max',
documentation:
'maximum (binary operator); if either operand is NaN, returns NaN',
kind: keyword,
insertText: 'f64.max'
},
{
label: 'i32.wrap/i64',
documentation: 'wrap a 64-bit integer to a 32-bit integer',
kind: keyword,
insertText: 'i32.wrap/i64'
},
{
label: 'i32.trunc_s/f32',
documentation: 'truncate a 32-bit float to a signed 32-bit integer',
kind: keyword,
insertText: 'i32.trunc_s/f32'
},
{
label: 'i32.trunc_s/f64',
documentation: 'truncate a 64-bit float to a signed 32-bit integer',
kind: keyword,
insertText: 'i32.trunc_s/f64'
},
{
label: 'i32.trunc_u/f32',
documentation: 'truncate a 32-bit float to an unsigned 32-bit integer',
kind: keyword,
insertText: 'i32.trunc_u/f32'
},
{
label: 'i32.trunc_u/f64',
documentation: 'truncate a 64-bit float to an unsigned 32-bit integer',
kind: keyword,
insertText: 'i32.trunc_u/f64'
},
{
label: 'i32.reinterpret/f32',
documentation:
'reinterpret the bits of a 32-bit float as a 32-bit integer',
kind: keyword,
insertText: 'i32.reinterpret/f32'
},
{
label: 'i64.extend_s/i32',
documentation: 'extend a signed 32-bit integer to a 64-bit integer',
kind: keyword,
insertText: 'i64.extend_s/i32'
},
{
label: 'i64.extend_u/i32',
documentation: 'extend an unsigned 32-bit integer to a 64-bit integer',
kind: keyword,
insertText: 'i64.extend_u/i32'
},
{
label: 'i64.trunc_s/f32',
documentation: 'truncate a 32-bit float to a signed 64-bit integer',
kind: keyword,
insertText: 'i64.trunc_s/f32'
},
{
label: 'i64.trunc_s/f64',
documentation: 'truncate a 64-bit float to a signed 64-bit integer',
kind: keyword,
insertText: 'i64.trunc_s/f64'
},
{
label: 'i64.trunc_u/f32',
documentation: 'truncate a 32-bit float to an unsigned 64-bit integer',
kind: keyword,
insertText: 'i64.trunc_u/f32'
},
{
label: 'i64.trunc_u/f64',
documentation: 'truncate a 64-bit float to an unsigned 64-bit integer',
kind: keyword,
insertText: 'i64.trunc_u/f64'
},
{
label: 'i64.reinterpret/f64',
documentation:
'reinterpret the bits of a 64-bit float as a 64-bit integer',
kind: keyword,
insertText: 'i64.reinterpret/f64'
},
{
label: 'f32.demote/f64',
documentation: 'demote a 64-bit float to a 32-bit float',
kind: keyword,
insertText: 'f32.demote/f64'
},
{
label: 'f32.convert_s/i32',
documentation: 'convert a signed 32-bit integer to a 32-bit float',
kind: keyword,
insertText: 'f32.convert_s/i32'
},
{
label: 'f32.convert_s/i64',
documentation: 'convert a signed 64-bit integer to a 32-bit float',
kind: keyword,
insertText: 'f32.convert_s/i64'
},
{
label: 'f32.convert_u/i32',
documentation: 'convert an unsigned 32-bit integer to a 32-bit float',
kind: keyword,
insertText: 'f32.convert_u/i32'
},
{
label: 'f32.convert_u/i64',
documentation: 'convert an unsigned 64-bit integer to a 32-bit float',
kind: keyword,
insertText: 'f32.convert_u/i64'
},
{
label: 'f32.reinterpret/i32',
documentation:
'reinterpret the bits of a 32-bit integer as a 32-bit float',
kind: keyword,
insertText: 'f32.reinterpret/i32'
},
{
label: 'f64.promote/f32',
documentation: 'promote a 32-bit float to a 64-bit float',
kind: keyword,
insertText: 'f64.promote/f32'
},
{
label: 'f64.convert_s/i32',
documentation: 'convert a signed 32-bit integer to a 64-bit float',
kind: keyword,
insertText: 'f64.convert_s/i32'
},
{
label: 'f64.convert_s/i64',
documentation: 'convert a signed 64-bit integer to a 64-bit float',
kind: keyword,
insertText: 'f64.convert_s/i64'
},
{
label: 'f64.convert_u/i32',
documentation: 'convert an unsigned 32-bit integer to a 64-bit float',
kind: keyword,
insertText: 'f64.convert_u/i32'
},
{
label: 'f64.convert_u/i64',
documentation: 'convert an unsigned 64-bit integer to a 64-bit float',
kind: keyword,
insertText: 'f64.convert_u/i64'
},
{
label: 'f64.reinterpret/i64',
documentation:
'reinterpret the bits of a 64-bit integer as a 64-bit float',
kind: keyword,
insertText: 'f64.reinterpret/i64'
},
{
label: 'current_memory',
documentation: 'current memory size in 64k pages',
kind: keyword,
insertText: 'current_memory'
},
{
label: 'grow_memory',
documentation: 'grow memory size by the specified amount of 64k pages',
kind: keyword,
insertText: 'grow_memory'
}
]);
}
const LanguageConfiguration: IRichLanguageConfiguration = {
// the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\'\,\.\<\>\/\?\s]+)/g,
comments: {
lineComment: ';;'
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')']
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: "'", close: "'" },
{ open: "'", close: "'" }
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: "'", close: "'" },
{ open: "'", close: "'" },
{ open: '<', close: '>' }
]
};
const MonarchDefinitions = {
// Set defaultToken to invalid to see what you do not tokenize yet
// defaultToken: 'invalid',
keywords: [
'module',
'table',
'memory',
'export',
'import',
'func',
'result',
'offset',
'anyfunc',
'type',
'data',
'start',
'element',
'global',
'local',
'mut',
'param',
'result',
'i32.load8_s',
'i32.load8_u',
'i32.load16_s',
'i32.load16_u',
'i32.load',
'i64.load8_s',
'i64.load8_u',
'i64.load16_s',
'i64.load16_u',
'i64.load32_s',
'i64.load32_u',
'i64.load',
'f32.load',
'f64.load',
'i32.store8',
'i32.store16',
'i32.store',
'i64.store8',
'i64.store16',
'i64.store32',
'i64.store',
'f32.store',
'f64.store',
'i32.const',
'i64.const',
'f32.const',
'f64.const',
'i32.add',
'i32.sub',
'i32.mul',
'i32.div_s',
'i32.div_u',
'i32.rem_s',
'i32.rem_u',
'i32.and',
'i32.or',
'i32.xor',
'i32.shl',
'i32.shr_u',
'i32.shr_s',
'i32.rotl',
'i32.rotr',
'i32.eq',
'i32.ne',
'i32.lt_s',
'i32.le_s',
'i32.lt_u',
'i32.le_u',
'i32.gt_s',
'i32.ge_s',
'i32.gt_u',
'i32.ge_u',
'i32.clz',
'i32.ctz',
'i32.popcnt',
'i32.eqz',
'f32.add',
'f32.sub',
'f32.mul',
'f32.div',
'f32.abs',
'f32.neg',
'f32.copysign',
'f32.ceil',
'f32.floor',
'f32.trunc',
'f32.nearest',
'f32.eq',
'f32.ne',
'f32.lt',
'f32.le',
'f32.gt',
'f32.ge',
'f32.sqrt',
'f32.min',
'f32.max',
'f64.add',
'f64.sub',
'f64.mul',
'f64.div',
'f64.abs',
'f64.neg',
'f64.copysign',
'f64.ceil',
'f64.floor',
'f64.trunc',
'f64.nearest',
'f64.eq',
'f64.ne',
'f64.lt',
'f64.le',
'f64.gt',
'f64.ge',
'f64.sqrt',
'f64.min',
'f64.max',
'i32.wrap/i64',
'i32.trunc_s/f32',
'i32.trunc_s/f64',
'i32.trunc_u/f32',
'i32.trunc_u/f64',
'i32.reinterpret/f32',
'i64.extend_s/i32',
'i64.extend_u/i32',
'i64.trunc_s/f32',
'i64.trunc_s/f64',
'i64.trunc_u/f32',
'i64.trunc_u/f64',
'i64.reinterpret/f64',
'f32.demote/f64',
'f32.convert_s/i32',
'f32.convert_s/i64',
'f32.convert_u/i32',
'f32.convert_u/i64',
'f32.reinterpret/i32',
'f64.promote/f32',
'f64.convert_s/i32',
'f64.convert_s/i64',
'f64.convert_u/i32',
'f64.convert_u/i64',
'f64.reinterpret/i64',
'local.get',
'local.set',
'local.tee',
'global.get',
'global.set',
'global.tee',
'get_local',
'set_local',
'tee_local',
'get_global',
'set_global',
'current_memory',
'grow_memory'
],
typeKeywords: ['i32', 'i64', 'f32', 'f64', 'anyfunc'],
operators: [] as any,
brackets: [
['(', ')', 'bracket.parenthesis'],
['{', '}', 'bracket.curly'],
['[', ']', 'bracket.square']
],
// we include these common regular expressions
symbols: /[=> x.label === word;
const item = watCompletionItems.find(predicate);
if (!item) {
return;
}
return {
range: new monaco.Range(
position.lineNumber,
index + 1,
position.lineNumber,
index + 1 + word.length
),
contents: [
'**DETAILS**',
{ language: 'html', value: item.documentation }
]
};
}
}
};
monaco.languages.onLanguage('wat', () => {
monaco.languages.setMonarchTokensProvider(
'wat',
Wat.MonarchDefinitions as any
);
monaco.languages.setLanguageConfiguration('wat', Wat.LanguageConfiguration);
monaco.languages.registerCompletionItemProvider(
'wat',
Wat.CompletionItemProvider
);
monaco.languages.registerHoverProvider('wat', Wat.HoverProvider as any);
});
monaco.languages.register({
id: 'wat'
});
monaco.languages.registerCompletionItemProvider(
'wat',
{
provideCompletionItems: function() {
return {
suggestions: [
{
label: 'const',
kind: monaco.languages.CompletionItemKind.Function,
documentation: 'i32',
insertText: 'i32.const ${1:value}',
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'function',
kind: monaco.languages.CompletionItemKind.Keyword,
documentation: 'i32',
insertText: '(func $${1:name} ${2:params} (result i32)\n ${3}\n)',
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'add',
kind: monaco.languages.CompletionItemKind.Keyword,
documentation: 'i32',
insertText: 'i32.add',
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'param',
kind: monaco.languages.CompletionItemKind.Keyword,
documentation: 'i32',
insertText: '(param $${1:name} i32) ',
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'local.get',
kind: monaco.languages.CompletionItemKind.Keyword,
documentation: 'i32',
insertText: 'local.get $${1:name}',
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet
},
{
label: 'global.get',
kind: monaco.languages.CompletionItemKind.Keyword,
documentation: 'i32',
insertText: 'global.get $${1:name}',
insertTextRules: CompletionItemInsertTextRule.InsertAsSnippet
}
]
};
}
},
'('
);
================================================
FILE: apps/kirjs/src/app/modules/webassembly/samples/answer.wat
================================================
(module
(import "config" "rowSize" (global $rowSize i32))
(memory 1)
(export "memory" (memory 0))
(table 8 anyfunc)
(func $enable (result i32)
(i32.const 1)
)
(func $disable (result i32)
(i32.const 0)
)
(elem (i32.const 0)
$enable ;; 000
$enable ;; 001
$enable ;; 010
$enable ;; 011
$enable ;; 100
$enable ;; 101
$enable ;; 110
$enable ;; 111
)
(type $return_i32 (func (result i32)))
(global $step (export "step") (mut i32) (i32.const 1))
(func $rotate (param $x i32) (result i32)
local.get $x
global.get $rowSize
i32.add
global.get $rowSize
i32.rem_s
)
(func $getIndex (param $x i32) (param $y i32) (result i32)
local.get $x
local.get $y
global.get $rowSize
i32.mul
i32.add
)
(func $loadCell (param $x i32) (param $y i32) (result i32)
(local.get $x)
call $rotate
(local.get $y)
call $getIndex
i32.const 4
i32.mul
i32.load
)
(func $storeCell (param $x i32) (param $value i32)
local.get $x
global.get $step
call $getIndex
i32.const 4
i32.mul
local.get $value
i32.store
)
(func $shift (param $a i32) (param $b i32) (param $c i32) (result i32)
local.get $a
(i32.const 4)
i32.mul
local.get $b
(i32.const 2)
i32.mul
local.get $c
i32.add
i32.add
)
(func $loadPreviousCell (param $x i32) (result i32)
local.get $x
global.get $step
(i32.const 1)
i32.sub
call $loadCell
)
(func $getCellScore (param $x i32) (result i32)
local.get $x
i32.const 1
i32.sub
call $loadPreviousCell
local.get $x
call $loadPreviousCell
local.get $x
i32.const 1
i32.add
call $loadPreviousCell
call $shift
)
(func $evolveCell (param $x i32)
local.get $x
local.get $x
call $getCellScore
call_indirect (type $return_i32)
call $storeCell
)
(func $evolveRow (local $i i32)
block
loop
local.get $i
call $evolveCell
local.get $i
(i32.const 1)
i32.add
local.tee $i
global.get $rowSize
i32.eq
br_if 1
br 0
end
end
)
(func $evolve (export "evolve") (param $steps i32) (local $i i32)
block
loop
call $evolveRow
global.get $step
i32.const 1
i32.add
global.set $step
local.get $i
(i32.const 1)
i32.add
tee_local $i
local.get $steps
i32.eq
br_if 1
br 0
end
end
)
)
================================================
FILE: apps/kirjs/src/app/modules/webassembly/samples/base.js
================================================
const size = 80;
const config = {
rowSize: 5,
steps: 100,
log: console.log
};
function drawOnCanvas(canvas, arr) {
const context = canvas.getContext('2d');
for (let i = 0; i < arr.length; i++) {
const x = i % config.rowSize;
const y = Math.floor(i / config.rowSize);
context.fillStyle = arr[i] ? 'lime' : '#444';
context.fillRect(x * size, y * size, size - 1, size - 1);
}
}
async function run(code, canvas) {
const result = await WebAssembly.instantiate(code, { config });
let memory = new Uint32Array(result.instance.exports.memory.buffer);
memory[Math.round(config.rowSize / 2)] = 1;
const r = result.instance.exports.evolve(config.steps);
drawOnCanvas(canvas, memory);
return r;
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/samples/base.wat
================================================
(module
)
================================================
FILE: apps/kirjs/src/app/modules/webassembly/samples/old.wat
================================================
(module
(import "config" "rowSize" (global $rowSize i32))
(import "config" "log" (func $log (param i32)))
(type $return_i32 (func (result i32)))
(memory 1)
(export "memory" (memory 0))
(table 8 funcref)
(elem (i32.const 0)
$remove ;; 000
$remove ;; 001
$create ;; 010
$create ;; 011
$create ;; 100
$create ;; 101
$remove ;; 110
$remove ;; 111
)
(func $create (result i32)
i32.const 1)
(func $remove (result i32)
i32.const 0)
(global $step (export "step") (mut i32) (i32.const 1))
(func $getIndex (param $x i32) (param $y i32) (result i32)
(i32.mul
(i32.const 4)
(i32.add
(local.get $x)
(i32.mul
(local.get $y)
(global.get $rowSize))))
)
(func $getCurrentIndex (param $x i32) (result i32)
(call $getIndex (local.get $x) (global.get $step))
)
(func $getPreviousIndex (param $x i32) (result i32)
(call $getIndex
(local.get $x)
(i32.sub
(global.get $step)
(i32.const 1)))
)
(func $rotate (param $x i32) (result i32)
(i32.rem_s
(i32.add
(global.get $rowSize)
(local.get $x))
(global.get $rowSize)
)
)
(func $shift (param $a i32) (param $b i32) (param $c i32) (result i32)
(local.get $c)
(local.get $b)
(i32.const 1)
i32.shl
i32.add
(local.get $a)
(i32.const 2)
i32.shl
i32.add
)
(func $calcNextState (param $x i32) (result i32)
local.get $x
call $getCellScore
call_indirect (type $return_i32)
)
(func $getCellScore (param $x i32) (result i32) (local $a i32)
local.get $x
i32.const 1
i32.sub
call $rotate
call $getPreviousIndex
i32.load
local.get $x
call $rotate
call $getPreviousIndex
i32.load
local.get $x
i32.const 1
i32.add
call $rotate
call $getPreviousIndex
i32.load
call $shift
)
(func $evolve (param $steps i32) (local $index i32)
block
loop
(call $evolveSingle)
;; $index++
local.get $index
i32.const 1
i32.add
local.tee $index
(br_if 1 (i32.eq (local.get $steps) (local.get $index)))
drop
br 0
end
end
)
(func $evolveSingle (result i32) (local $index i32)
block
loop
;; color up the cell
local.get $index
call $getCurrentIndex
local.get $index
call $calcNextState
i32.store
;; $index++
local.get $index
i32.const 1
i32.add
local.tee $index
(br_if 1 (i32.eq (global.get $rowSize) (local.get $index)))
drop
br 0
end
end
global.get $step
i32.const 1
i32.add
global.set $step
local.get $index
)
(export "evolve" (func $evolve))
)
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/add-tests.ts
================================================
export const addTests = [
{
args: [0, 0],
output: 0
},
{
args: [1, 0],
output: 1
},
{
args: [1, 1],
output: 2
},
{
args: [17, 25],
output: 42
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/common.ts
================================================
export const red = '#ff3f00';
export const defaultRowSize = 5;
export function colorMatchesExpected(a, i, c, test) {
return test.actualMemory[i] === test.expectedMemory[i] ? '#ddd' : red;
}
export function outputCoordinates(config) {
return [
{
y: -1,
x: config.args[0],
color: 'yellow',
text: config.args[0]
},
{
x: -1,
y: config.args[1],
color: 'yellow',
text: config.args[1]
},
{
x: config.args[0],
y: config.args[1],
color: 'lime',
text: config.output
}
];
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/disable-tests.ts
================================================
export const disableTests = [
{
args: [],
output: 0
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/enable-tests.ts
================================================
export const enableTests = [
{
args: [],
output: 1
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/evolve-cell.ts
================================================
import { colorMatchesExpected } from './common';
const rowSize = 3;
const viz = {
type: 'evolve',
rowSize,
text: a => a,
memory: test => test.actualMemory,
color: colorMatchesExpected
};
export const evolveCellTests = [
{
args: [0],
memory: [0, 0, 0, 0, 0, 0],
table: [
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable'
],
expectedMemory: [0, 0, 0, 1, 0, 0],
imports: { config: { step: 1, rowSize: 3 } },
viz: {
...viz,
rule: 0
}
},
{
args: [2],
memory: [0, 0, 0, 0, 0, 0],
table: [
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable'
],
expectedMemory: [0, 0, 0, 0, 0, 1],
imports: { config: { step: 1, rowSize: 3 } },
viz
},
{
args: [2],
memory: [1, 1, 1, 1, 1, 1],
table: [
'disable',
'disable',
'disable',
'disable',
'disable',
'disable',
'disable',
'disable'
],
expectedMemory: [1, 1, 1, 1, 1, 0],
imports: { config: { step: 1, rowSize: 3 } },
viz
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/evolve-row.ts
================================================
import { colorMatchesExpected } from './common';
const rowSize = 3;
const viz = {
type: 'evolve',
rowSize,
text: a => a,
memory: test => test.actualMemory,
color: colorMatchesExpected
};
export const evolveRowTests = [
{
args: [],
memory: [0, 0, 0, 0, 0, 0],
table: [
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable'
],
expectedMemory: [0, 0, 0, 1, 1, 1],
imports: { config: { step: 1, rowSize: 3 } },
viz
},
{
args: [2],
memory: [1, 1, 1, 1, 1, 1],
table: [
'disable',
'disable',
'disable',
'disable',
'disable',
'disable',
'disable',
'disable'
],
expectedMemory: [1, 1, 1, 0, 0, 0],
imports: { config: { step: 1, rowSize: 3 } },
viz
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/evolve.ts
================================================
import { colorMatchesExpected, defaultRowSize } from './common';
const viz = {
type: 'evolve',
rowSize: defaultRowSize,
text: a => a,
memory: test => test.actualMemory,
color: colorMatchesExpected
};
export const evolveTests = [
{
args: [1],
memory: [0, 0, 0, 1, 0],
table: [
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable',
'enable'
],
expectedMemory: [0, 0, 0, 1, 0, 1, 1, 1, 1, 1],
imports: { config: { step: 1, rowSize: defaultRowSize } },
viz
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/get-cell-score.ts
================================================
export const getCellScoreTests = [
{
args: [1],
memory: [0, 0, 1],
output: 0b001,
imports: { config: { rowSize: 3, step: 1 } }
},
{
args: [2],
memory: [1, 0, 0],
output: 0b001,
imports: { config: { rowSize: 3, step: 1 } }
},
{
args: [1],
memory: [1, 1, 0],
output: 0b110,
imports: { config: { rowSize: 3, step: 1 } }
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/get-index.ts
================================================
import { outputCoordinates } from './common';
const rowSize = 5;
const viz = {
rowSize,
text: (a, i) => i,
extras: outputCoordinates
};
export const getIndex = [
{
args: [0, 0],
imports: { config: { rowSize: rowSize } },
output: 0,
viz
},
{
args: [1, 0],
imports: { config: { rowSize: rowSize } },
output: 1,
viz
},
{
args: [0, 1],
imports: { config: { rowSize: rowSize } },
output: rowSize,
viz
},
{
args: [0, 1],
imports: { config: { rowSize: 4 } },
output: 4,
viz
},
{
args: [3, 3],
imports: { config: { rowSize: 4 } },
output: 15,
viz
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/load-cell.ts
================================================
const rowSize = 2;
export const memoryRepViz = {
rowSize: rowSize * 4,
text: (a, i, c, test) => {
return i % 4 === 0 ? test.memory[i / 4] : '';
},
color: (a, i, o, test) => {
const c = (test.args[1] * rowSize + test.args[0]) * 4;
if (i >= c && i < c + 4) {
return 'lime';
}
return Math.floor((i / 4) % 2) === 0 ? '#ddd' : '#999';
}
};
export const loadCellTests = [
{
args: [0, 0],
memory: [0, 0, 0],
output: 0,
imports: { config: { rowSize } },
viz: memoryRepViz
},
{
args: [0, 0],
memory: [1, 0, 0],
output: 1,
imports: { config: { rowSize } },
viz: memoryRepViz
},
{
args: [1, 0],
memory: [1, 1, 0, 0],
output: 1,
imports: { config: { rowSize } },
viz: memoryRepViz
},
{
args: [0, 1],
memory: [1, 0, 1, 0],
viz: memoryRepViz,
output: 1,
imports: { config: { rowSize } }
},
{
args: [1, 1],
memory: [1, 0, 0, 0],
output: 0,
viz: memoryRepViz,
imports: { config: { rowSize } }
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/load-previous-cell.ts
================================================
export const loadPreviousCellTests = [
{
args: [0],
memory: [0, 0, 0],
output: 0,
imports: { config: { step: 1, rowSize: 3 } }
},
{
args: [0],
memory: [1, 0, 0],
output: 1,
imports: { config: { step: 1, rowSize: 3 } }
},
{
args: [2],
memory: [1, 0, 1],
output: 1,
imports: { config: { step: 1, rowSize: 3 } }
},
{
args: [2],
memory: [1, 0, 1, 1, 0, 1],
output: 1,
imports: { config: { step: 2, rowSize: 3 } }
},
{
args: [2],
memory: [1, 0, 1, 1, 0, 0],
output: 0,
imports: { config: { step: 2, rowSize: 3 } }
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/rotate.ts
================================================
const rowSize = 5;
const longFowSize = 4;
const yellow = {
color: 'yellow',
text: 'Y'
};
const lime = {
color: 'lime',
text: 'G'
};
const red = {
color: 'red',
text: 'R'
};
export const rotate = [
{
args: [0, rowSize],
imports: { config: { rowSize } },
output: 0,
viz: {
rowSize,
extras: [
{
...lime,
x: 0,
y: 0
},
{
...yellow,
x: 0,
y: 1
}
]
}
},
{
args: [rowSize, rowSize],
imports: { config: { rowSize } },
output: 0,
viz: {
rowSize,
extras: [
{
...red,
x: rowSize,
y: 0
},
{
...lime,
x: 0,
y: 0
},
{
...lime,
x: rowSize - 2,
y: 0
},
{
...lime,
x: rowSize - 1,
y: 0
},
{
...yellow,
x: rowSize - 1,
y: 1
}
]
}
},
{
args: [-1, rowSize],
imports: { config: { rowSize } },
output: 4,
viz: {
rowSize,
extras: [
{
...red,
x: -1,
y: 0
},
{
...lime,
x: 0,
y: 0
},
{
...lime,
x: 1,
y: 0
},
{
...lime,
x: rowSize - 1,
y: 0
},
{
...yellow,
x: 0,
y: 1
}
]
}
},
{
args: [longFowSize, longFowSize],
imports: { config: { rowSize: longFowSize } },
output: 0,
viz: {
rowSize: longFowSize,
extras: [
{
...red,
x: longFowSize,
y: 0
},
{
...lime,
x: 0,
y: 0
},
{
...lime,
x: longFowSize - 2,
y: 0
},
{
...lime,
x: longFowSize - 1,
y: 0
},
{
...yellow,
x: longFowSize - 1,
y: 1
}
]
}
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/shift-tests.ts
================================================
const viz = {
type: 'shift'
};
export const shiftTests = [
{
args: [0, 0, 0],
output: 0,
viz
},
{
args: [0, 0, 1],
output: 0b1,
viz
},
{
args: [0, 1, 0],
output: 0b10,
viz
},
{
args: [1, 0, 0],
output: 0b100,
viz
},
{
args: [1, 1, 1],
output: 0b111,
viz
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/tests/store-cell-tests.ts
================================================
import { memoryRepViz } from './load-cell';
export const storeCellTests = [
{
args: [1, 1],
imports: { config: { rowSize: 3, step: 0 } },
memory: [0, 1, 0],
expectedMemory: [0, 1, 0],
viz: memoryRepViz
},
{
args: [1, 0],
imports: { config: { rowSize: 3, step: 0 } },
memory: [0, 1, 0],
expectedMemory: [0, 0, 0],
viz: memoryRepViz
}
];
================================================
FILE: apps/kirjs/src/app/modules/webassembly/utils.spec.ts
================================================
import { extractFunction } from './utils';
describe('SyncComponent', () => {
it('should create', () => {
expect(
extractFunction(
'getIndex',
` (export "memory" (memory 0))
(global $step (export "step") (mut i32) (i32.const 1))
(func $getIndex (param $x i32) (param $y i32) (result i32)
(i32.mul
(i32.const 4)
(i32.add
(local.get $x)
(i32.mul
(local.get $y)
(global.get $rowSize))))
)
`
)
).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/utils.ts
================================================
import {
BaseBlock,
CodeHelperBlock
} from './webassembly-playground/monaco-directives/common';
export function extractFunction(name, code) {
return extractExpressionByMatch(
new RegExp('\\(func \\$' + name + '\\b'),
code
);
}
export function prepareTableCode(code) {
const elements = extractExpressionByMatch(/\(elem/, code);
if (!elements) {
// tslint:disable-next-line:no-debugger
debugger;
}
const functions = [
...new Set([...elements.matchAll(/\$(\w+)\b/g)].map(a => a[1]))
]
.map(name => extractFunction(name, code))
.join('\n');
const tableDef = extractExpressionByMatch(/\(table/, code);
return `
;; table
${tableDef}
;; elem
${elements}
;; table functions
${functions}
`;
}
export function extractTypeCode(code) {
// TODO(kirjs): This would only extract one type, at some point there can be more
const type = extractExpressionByMatch(/\(type/, code);
return `
;; types (we only extract one ATM)
${type}
`;
}
const matchTypeRegex = /^\(\s*([\w.]+)\b/;
const funcNameRegex = /func\s*\$(\w+)/;
function getName(code) {
if (getType(code) === 'func') {
const match = code.match(funcNameRegex);
return match ? match[1] : undefined;
}
return undefined;
}
function getType(code) {
const t = code.match(matchTypeRegex);
return t && t[1];
}
export function serializeBlocks(blocks: CodeHelperBlock[]) {
return blocks.map(b => b.type + (b.name ? `(${b.name})` : '')).join('/');
}
export function populateBlocks(blocks: BaseBlock[]): CodeHelperBlock[] {
return blocks.map((b: any) => {
const type = getType(b.code) || 'module';
return {
name: getName(b.code),
type,
...b
};
});
}
export function extractBlocks(
textBefore,
textAfter,
prependLeft = '',
prependRight = ''
) {
const before = findPrevNonMatchingClosingBrace(textBefore);
const after = findNextNonMatchingClosingBrace(textAfter);
if (before && after) {
const next = extractBlocks(
textBefore.slice(0, -before.length - 1),
textAfter.slice(after.length),
before + prependLeft,
prependRight + after
);
return [
{
before,
after,
code: before + prependLeft + prependRight + after
},
...next
];
}
return [];
}
export function findNextNonMatchingClosingBrace(code: string) {
return findMatchingBrace(code, 0, 1, 1);
}
export function findPrevNonMatchingClosingBrace(code: string) {
return findMatchingBrace(code, code.length - 1, -1, -1, -1);
}
function findMatchingBrace(
code: string,
startIndex = 0,
shift = 1,
braces = 0,
/*This is a hack, need proper fix*/ resultShift = 0
) {
let i = startIndex;
while (code[i]) {
const c = code[i];
if (c === '(') {
braces++;
}
if (c === ')') {
braces--;
}
i += shift;
if (braces === 0) {
return code.substring(startIndex, i - resultShift);
}
}
}
export function extractExpressionByMatch(regex, code) {
const match = regex.exec(code);
if (match) {
let i = match.index;
let braces = 0;
while (true) {
const c = code[i];
if (!c) {
return null;
}
if (c === '(') {
braces++;
}
if (c === ')') {
braces--;
}
i++;
if (braces === 0) {
return code.substring(match.index, i);
}
}
}
}
export function extractGlobalAccess(code) {
const match = /(?:get_global|global\.get)\s+\$(\w+)*/g;
return [...new Set([...code.matchAll(match)].map(a => a[1]))];
}
export function extractGlobals(code, allCode) {
return extractGlobalAccess(code);
}
export function hasMemoryCalls(code) {
return /i32\.(store|load)\s+/g.test(code);
}
export function hasTypeCalls(code) {
return /type \$/g.test(code);
}
export function hasTableCalls(code, config) {
return /(call_indirect)\s+/g.test(code);
}
function unique(arr) {
return [...new Set(arr)];
}
export function populateTestCode(
code: string,
test,
allCode: string,
table: string
) {
if (test.table) {
const funcs = unique(test.table)
.map(name => extractFunction(name, allCode))
.join('\n\n');
const elements = test.table.map(e => ' $' + e).join('\n');
table = `
(table ${test.table.length} funcref)
(elem (i32.const 0)
${elements}
)
${funcs}
`;
}
const regExp = /;;{table}/;
if (table) {
code = code.replace(regExp, table);
}
return code;
}
export function extractFunctionWithDependencies(
name,
code: string,
dependencies: string[]
) {
return extractFunctionDependencyNames(name, code, dependencies)
.map(name => extractFunction(name, code))
.join('\n\n');
}
export function hasGlobal(name, code) {
return /global\s+/g.test(code);
}
export function extractFunctionDependencyNames(
name,
code: string,
dependencies: string[]
) {
const funcCode = extractFunction(name, code);
const match = /(?:\bcall)\s+\$(\w+)*/g;
if (!funcCode) {
return [];
}
const functions = [
...new Set([...funcCode.matchAll(match)].map(a => a[1]))
].filter(d => !dependencies.includes(d));
const nestedDeps = functions.flatMap(f =>
extractFunctionDependencyNames(f, code, [...dependencies, ...functions])
);
return [...new Set([...dependencies, ...nestedDeps, ...functions])];
}
function getMemoryCode(hasMemory: boolean) {
return hasMemory
? `
(memory 1)
(export "memory" (memory 0))
`
: '';
}
export function generateWatTestCode({
code,
globals,
name,
hasMemory,
types
}: any) {
const globalsCode = globals
.map(
global =>
`(global \$${global} (export "${global}") (mut i32) (i32.const 0))`
)
.join('\n');
const memoryCode = getMemoryCode(hasMemory);
const funcExport = code.match(`export "${name}"`)
? ''
: `(export "${name}" (func $${name}))`;
return `(module
${funcExport}
${types}
${globalsCode}
;;{table}
${memoryCode}
${code}
)`;
}
export function extractAllFunctions(code) {
const match = /func\s+\$(\w+)*/g;
return [...new Set([...code.matchAll(match)].map(a => a[1]))];
}
export function extractAnswers(code) {
const functions = extractAllFunctions(code).map(name => [
name,
extractFunction(name, code)
]);
return new Map(functions as any);
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/wasm-binary/wasm-binary.component.css
================================================
::ng-deep [name='section-type'] {
display: block;
flex-basis: 100%;
background: #444 !important;
border-radius: 4px;
margin-top: 8px;
margin-bottom: 4px;
padding: 8px 16px;
color: white !important;
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/wasm-binary/wasm-binary.component.html
================================================
================================================
FILE: apps/kirjs/src/app/modules/webassembly/wasm-binary/wasm-binary.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { WasmBinaryComponent } from './wasm-binary.component';
describe('WasmBinaryComponent', () => {
let component: WasmBinaryComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [WasmBinaryComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(WasmBinaryComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/wasm-binary/wasm-binary.component.ts
================================================
import { Component } from '@angular/core';
import { wasmParser } from './wasm-parser';
import { strToBin } from '../../binary/parser/utils';
declare const require;
// if the extention is .wasm, webpack is being stupid.
const wasm = require('!binary-loader!./test._wasm');
@Component({
selector: 'kirjs-wasm-binary',
templateUrl: './wasm-binary.component.html',
styleUrls: ['./wasm-binary.component.css']
})
export class WasmBinaryComponent {
readonly binary = strToBin(wasm);
readonly parser = wasmParser();
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/wasm-binary/wasm-parser.ts
================================================
import { BinaryParser } from '../../binary/parser/binary-parser';
const funcTypes = {
0x7f: 'i32',
0x7e: 'i64',
0x7d: 'f32',
0x7c: 'f64',
0x70: 'anyfunc',
0x60: 'func',
0x40: 'empty_block'
};
const valueTypes = {
0x7f: 'i32',
0x7e: 'i64',
0x7d: 'f32',
0x7c: 'f64'
};
export function wasmParser() {
const funcParamTypeParser = new BinaryParser().uInt8('type', {
enum: valueTypes
});
const funcTypeParser = new BinaryParser()
.uInt8('type', { enum: funcTypes })
.uInt8('paramCount')
.array('params', {
parser: funcParamTypeParser,
length: 'paramCount'
})
.uInt8('returnCount')
.array('returns', {
parser: funcParamTypeParser,
length: 'returnCount'
});
const fieldParser = new BinaryParser()
.varuint7('len')
.string('name', { length: 'len' });
const kindParser = new BinaryParser().uInt8('kind').choice('declaration', {
key: 'kind',
values: {
0: new BinaryParser().varuint7('function'),
2: new BinaryParser().varuint7('memory-tbd')
}
});
const exportItemParser = new BinaryParser()
.block('field', fieldParser)
.block('kind', kindParser);
const localEntryParser = new BinaryParser()
.varuint7('local_count')
.uInt8('value_type');
const codeBodyParser = new BinaryParser()
.varuint7('function_body_type_body_size')
.block('local', localEntryParser)
.block('local', localEntryParser)
.block('local', localEntryParser)
.block('local', localEntryParser);
const sectionParsers = {
type: new BinaryParser().uInt8('count').array('params', {
length: 'count',
parser: funcTypeParser
}),
import: new BinaryParser()
.uInt8('count')
.varuint7('moduleLen')
.string('module', { length: 'moduleLen' })
.block('field', fieldParser)
.block('kind', kindParser),
function: new BinaryParser().bit32('TBD'),
table: new BinaryParser().bit24('TBD'),
export: new BinaryParser().varuint7('count').array('items', {
length: 'count',
parser: exportItemParser
}),
start: new BinaryParser().varuint7('index'),
code: new BinaryParser()
.varuint7('number_of_functions')
.block('lol', codeBodyParser)
};
const sectionParser = new BinaryParser()
.uInt8('section-type', {
enum: {
0x01: 'Types',
0x02: 'Import',
0x03: 'Function',
0x05: 'Table',
0x07: 'Export',
0x08: 'Start',
0x0a: 'Code'
}
})
.varuint7('size')
.choice('section', {
key: 'section-type',
values: {
0x01: sectionParsers.type,
0x02: sectionParsers.import,
0x03: sectionParsers.function,
0x05: sectionParsers.table,
0x07: sectionParsers.export,
0x08: sectionParsers.start,
0x0a: sectionParsers.code
}
});
const header = new BinaryParser()
.string('headerConst', {
length: 4,
description: `header const`
})
.uInt32le('version')
.array('sections', { length: 7, parser: sectionParser });
return new BinaryParser().block('header', header);
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/error-message/error-message.component.css
================================================
.error {
font-size: 20px;
padding: 20px;
text-align: center;
background: #ff4e3d;
color: #fff;
opacity: 0.9;
width: 100%;
margin-left: -20px;
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/error-message/error-message.component.html
================================================
{{ result.value }}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/error-message/error-message.component.spec.ts
================================================
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ErrorMessageComponent } from './error-message.component';
describe('ErrorMessageComponent', () => {
let component: ErrorMessageComponent;
let fixture: ComponentFixture;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ErrorMessageComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ErrorMessageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/error-message/error-message.component.ts
================================================
import { Component, Input, OnInit } from '@angular/core';
import { Result } from '../web-assembly.service';
@Component({
selector: 'slides-error-message',
templateUrl: './error-message.component.html',
styleUrls: ['./error-message.component.css']
})
export class ErrorMessageComponent implements OnInit {
@Input() result: Result;
constructor() {}
ngOnInit() {}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/monaco-directives/common.ts
================================================
import {
webAssemblyModuleContentHandler,
webAssemblyTestHandler
} from '../runners/wasm-test-runner/wasm-test-runner.component';
export interface BaseBlock {
code: string;
before?: string;
after?: string;
}
export interface CodeHelperBlock extends BaseBlock {
type: string;
name?: string;
meta: any;
}
export interface CodePath {
type: 'ts' | 'wat';
blocks: CodeHelperBlock[];
}
export function getCodeBlockHandler(lang, type) {
if (!codeBlockHandlers[lang] || !codeBlockHandlers[lang][type]) {
return;
}
return codeBlockHandlers[lang][type];
}
const displayPreview = () => {
return { mode: 'default' };
};
export const codeBlockHandlers = {
ts: {
FunctionDeclaration: displayPreview,
SourceFile: displayPreview
},
wat: {
elem: displayPreview,
func: webAssemblyTestHandler,
module: webAssemblyModuleContentHandler
}
};
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/monaco-directives/monaco-js-position.directive.ts
================================================
import {
AfterViewInit,
Directive,
EventEmitter,
Optional,
Output,
Self
} from '@angular/core';
import { CodeDemoEditorInjector } from '@codelab/code-demos/src/lib/code-demo-editor/code-demo-editor.injector';
import { getTypeScript } from '@codelab/utils/src/lib/loaders/loaders';
import { serializeBlocks } from '../../utils';
import { CodePath } from './common';
const ts = getTypeScript();
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed
});
const usefulTypes = new Set([
ts.SyntaxKind.SourceFile,
ts.SyntaxKind.FunctionDeclaration
]);
function getName(token) {
if (token.kind === ts.SyntaxKind.FunctionDeclaration) {
return token.name.text;
}
}
function resolveToken(token, sourceFile) {
const code = printer.printNode(ts.EmitHint.Unspecified, token, sourceFile);
const name = getName(token);
return {
code,
type: ts.SyntaxKind[token.kind],
...token,
name
};
}
function processBlocks(blocks: any[], sourceFile) {
return blocks
.map(b => resolveToken(b, sourceFile))
.filter(b => {
return usefulTypes.has(b.kind);
});
}
@Directive({
selector: '[slidesMonacoJsPosition]'
})
export class MonacoJsPositionDirective implements AfterViewInit {
@Output() slidesMonacoJsPosition = new EventEmitter();
lastResult: string;
constructor(
@Self() @Optional() private editorInjector: CodeDemoEditorInjector
) {}
ngAfterViewInit() {
const editor = this.editorInjector.editor;
editor.onDidChangeCursorPosition(({ position }) => {
const model = editor.getModel();
const value = model.getValue();
const sourceFile = ts.createSourceFile(
'file.ts',
value,
ts.ScriptTarget.ES2015,
/*setParentNodes */ true
);
const offset = model.getOffsetAt(position);
let token = (ts as any).getTokenAtPosition(sourceFile, offset);
const blocks = [token];
while (token.parent && token.kind) {
token = token.parent;
blocks.push(token);
}
const processedBlocks = processBlocks(blocks, sourceFile);
const result = serializeBlocks(processedBlocks);
// If we're in the same path and value is the same, let's not update
const key = result + value;
if (this.lastResult !== key) {
this.lastResult = key;
this.slidesMonacoJsPosition.emit({
type: 'ts',
blocks: processedBlocks
});
}
});
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/monaco-directives/monaco-load-answer.directive.ts
================================================
import { Directive, EventEmitter, Optional, Output, Self } from '@angular/core';
import { CodeDemoEditorInjector } from '@codelab/code-demos/src/lib/code-demo-editor/code-demo-editor.injector';
@Directive({
selector: '[slidesMonacoLoadAnswer]',
exportAs: 'MonacoWatLoadAnswer'
})
export class MonacoWatLoadAnswerDirective {
@Output() slidesMonacoLoadAnswer = new EventEmitter();
constructor(
@Self() @Optional() private editorInjector: CodeDemoEditorInjector
) {}
loadAnswer(config: any) {
const model = this.editorInjector.editor.getModel();
const value = model.getValue();
const newValue = value.replace(config.originalCode, config.answer);
model.setValue(newValue);
this.slidesMonacoLoadAnswer.emit();
const pos = model.getPositionAt(newValue.indexOf(config.answer) + 20);
this.editorInjector.editor.setPosition(pos);
this.editorInjector.editor.focus();
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/monaco-directives/monaco-scrolling.directive.ts
================================================
import { Directive, Input, OnChanges, Optional, Self } from '@angular/core';
import { CodeDemoEditorInjector } from '@codelab/code-demos/src/lib/code-demo-editor/code-demo-editor.injector';
import { findPosition } from '@codelab/code-demos/src/lib/code-demo-editor/utils/utils';
@Directive({
selector: '[slidesMonacoScrolling]'
})
export class MonacoScrollingDirective implements OnChanges {
@Input() slidesMonacoScrolling = '';
lastValue: string;
constructor(
@Self() @Optional() private editorInjector: CodeDemoEditorInjector
) {}
ngOnChanges(changes) {
const editor = this.editorInjector.editor;
if (
editor &&
changes.slidesMonacoScrolling &&
changes.slidesMonacoScrolling.currentValue !== this.lastValue
) {
this.lastValue = this.slidesMonacoScrolling;
if (this.slidesMonacoScrolling) {
const range = findPosition(
editor.getModel().getValue(),
this.slidesMonacoScrolling
);
// This does not really work
editor.revealRangeInCenter({
startColumn: range.indexStart,
endColumn: range.indexEnd,
startLineNumber: range.lineStart,
endLineNumber: range.lineEnd
});
}
}
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/monaco-directives/monaco-wat-position.directive.spec.ts
================================================
import { MonacoWatPositionDirective } from './monaco-cursor-position.directive';
describe('MonacoCursorPositionDirective', () => {
it('should create an instance', () => {
const directive = new MonacoWatPositionDirective();
expect(directive).toBeTruthy();
});
});
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/monaco-directives/monaco-wat-position.directive.ts
================================================
import {
AfterViewInit,
Directive,
EventEmitter,
Optional,
Output,
Self
} from '@angular/core';
import { CodeDemoEditorInjector } from '@codelab/code-demos/src/lib/code-demo-editor/code-demo-editor.injector';
import { extractBlocks, populateBlocks, serializeBlocks } from '../../utils';
import { CodePath } from './common';
@Directive({
selector: '[slidesMonacoWatPosition]'
})
export class MonacoWatPositionDirective implements AfterViewInit {
@Output() slidesMonacoWatPosition = new EventEmitter();
lastResult: string;
constructor(
@Self() @Optional() private editorInjector: CodeDemoEditorInjector
) {}
ngAfterViewInit() {
const editor = this.editorInjector.editor;
editor.onDidChangeCursorPosition(x => {
const position = x.position;
const model = editor.getModel();
const offset = model.getOffsetAt(position);
const value = model.getValue();
const textBefore = value.slice(0, offset + 1);
const textAfter = value.slice(offset);
const blocks = populateBlocks(extractBlocks(textBefore, textAfter));
const result = serializeBlocks(blocks);
// If we're in the same path and value is the same, let's not update
const key = result + value;
if (this.lastResult !== key) {
this.lastResult = key;
this.slidesMonacoWatPosition.emit({ type: 'wat', blocks });
}
});
}
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/runners/wasm-test-runner/runner.js
================================================
async function run(code, { args, imports, name, memory }) {
// imports.config.log = console.log;
const program = await WebAssembly.instantiate(code, imports);
if (memory) {
if (!program.instance.exports.memory) {
throw new Error(
'This test expects memory to be exported from WebAssembly, but none was exported.'
);
}
const mem = new Uint32Array(program.instance.exports.memory.buffer);
for (let m = 0; m < memory.length; m++) {
mem[m] = memory[m];
}
}
if (imports) {
Object.entries(imports.config).map(([key, value]) => {
if (program.instance.exports[key]) {
program.instance.exports[key].value = value;
}
});
}
const result = program.instance.exports[name](...args);
return {
result,
exports: program.instance.exports
};
}
================================================
FILE: apps/kirjs/src/app/modules/webassembly/webassembly-playground/runners/wasm-test-runner/wasm-test-runner.component.html
================================================