├── .editorconfig ├── .gitignore ├── .prettierrc ├── .releaserc ├── .travis.yml ├── .vscode └── launch.json ├── README.md ├── angular.json ├── config ├── cmd.ts ├── dashboard.ts ├── default.ts └── proxy.conf.js ├── gulpfile.ts ├── package-lock.json ├── package.json ├── projects ├── demo │ ├── browserslist │ ├── src │ │ ├── app │ │ │ ├── app.component.html │ │ │ ├── app.component.scss │ │ │ ├── app.component.ts │ │ │ ├── app.module.ts │ │ │ └── example-mocks │ │ │ │ ├── http.mock.ts │ │ │ │ ├── index.ts │ │ │ │ └── system.mock.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── oauth-resp.html │ │ ├── polyfills.ts │ │ ├── shared.styles.scss │ │ └── styles.css │ └── tsconfig.app.json └── library │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ ├── lib │ │ ├── directives │ │ │ ├── binding.directive.spec.ts │ │ │ └── binding.directive.ts │ │ ├── library.module.ts │ │ ├── services │ │ │ ├── binding.service.spec.ts │ │ │ ├── binding.service.ts │ │ │ ├── composer.service.spec.ts │ │ │ ├── composer.service.ts │ │ │ ├── http.service.spec.ts │ │ │ └── http.service.ts │ │ └── settings.ts │ ├── oauth-resp.html │ ├── public-api.ts │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | *.tgz 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "tabWidth": 4, 4 | "printWidth": 100 5 | } -------------------------------------------------------------------------------- /.releaserc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@semantic-release/commit-analyzer", 4 | "@semantic-release/release-notes-generator", 5 | ["@semantic-release/npm", { 6 | "pkgRoot": "dist/library" 7 | }], 8 | ["@semantic-release/git", { 9 | "assets": ["package.json", "npm-shrinkwrap.json"] 10 | }] 11 | ] 12 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | addons: 3 | chrome: stable 4 | cache: 5 | directories: 6 | - ~/.npm 7 | notifications: 8 | email: false 9 | node_js: 10 | - '12' 11 | script: 12 | - npm run test:ci && gulp build 13 | after_success: 14 | - if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run semantic-release; fi 15 | branches: 16 | except: 17 | - /^v\d+\.\d+\.\d+$/ 18 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "chrome", 9 | "request": "launch", 10 | "name": "Launch Chrome against localhost", 11 | "url": "http://localhost:4200", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular 8+ Composer Library 2 | 3 | [![Travis](https://travis-ci.org/acaengine/composer.svg)](https://travis-ci.org/acaengine/composer) 4 | [![npm version](https://badge.fury.io/js/%40acaengine%2Fcomposer.svg)](https://badge.fury.io/js/%40acaengine%2Fcomposer) 5 | [![david-dm](https://david-dm.org/acaengine/composer.svg)](https://david-dm.org/acaengine/composer) 6 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 7 | 8 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.0. 9 | 10 | ## Development server 11 | 12 | Run `gulp serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 13 | 14 | ## Build 15 | 16 | Run `gulp build` to build the library. The build artifacts will be stored in the `dist/library` directory. Use the `--prod` flag for a production build. 17 | 18 | ## Running unit tests 19 | 20 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 21 | 22 | ## Installation 23 | 24 | You install the library from npm using `npm install @acaengine/composer`; 25 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "library": { 7 | "projectType": "library", 8 | "root": "projects/library", 9 | "sourceRoot": "projects/library/src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-ng-packagr:build", 14 | "options": { 15 | "tsConfig": "projects/library/tsconfig.lib.json", 16 | "project": "projects/library/ng-package.json" 17 | }, 18 | "assets": [ 19 | "projects/library/src/oauth-resp.html" 20 | ] 21 | }, 22 | "test": { 23 | "builder": "@angular-devkit/build-angular:karma", 24 | "options": { 25 | "main": "projects/library/src/test.ts", 26 | "tsConfig": "projects/library/tsconfig.spec.json", 27 | "karmaConfig": "projects/library/karma.conf.js" 28 | } 29 | }, 30 | "lint": { 31 | "builder": "@angular-devkit/build-angular:tslint", 32 | "options": { 33 | "tsConfig": [ 34 | "projects/library/tsconfig.lib.json", 35 | "projects/library/tsconfig.spec.json" 36 | ], 37 | "exclude": [ 38 | "**/node_modules/**" 39 | ] 40 | } 41 | } 42 | } 43 | }, 44 | "demo": { 45 | "projectType": "application", 46 | "schematics": { 47 | "@schematics/angular:class": { 48 | "skipTests": true 49 | }, 50 | "@schematics/angular:component": { 51 | "skipTests": true 52 | }, 53 | "@schematics/angular:directive": { 54 | "skipTests": true 55 | }, 56 | "@schematics/angular:guard": { 57 | "skipTests": true 58 | }, 59 | "@schematics/angular:module": { 60 | "skipTests": true 61 | }, 62 | "@schematics/angular:pipe": { 63 | "skipTests": true 64 | }, 65 | "@schematics/angular:service": { 66 | "skipTests": true 67 | } 68 | }, 69 | "root": "projects/demo", 70 | "sourceRoot": "projects/demo/src", 71 | "prefix": "app", 72 | "architect": { 73 | "build": { 74 | "builder": "@angular-devkit/build-angular:browser", 75 | "options": { 76 | "outputPath": "dist/demo", 77 | "index": "projects/demo/src/index.html", 78 | "main": "projects/demo/src/main.ts", 79 | "polyfills": "projects/demo/src/polyfills.ts", 80 | "tsConfig": "projects/demo/tsconfig.app.json", 81 | "assets": [ 82 | "projects/demo/src/favicon.ico", 83 | "projects/demo/src/assets", 84 | "projects/demo/src/oauth-resp.html" 85 | ], 86 | "stylePreprocessorOptions": { 87 | "includePaths": [ 88 | "projects/demo/src", 89 | "node_modules" 90 | ] 91 | }, 92 | "styles": [ 93 | "projects/demo/src/styles.css" 94 | ], 95 | "scripts": [] 96 | }, 97 | "configurations": { 98 | "production": { 99 | "fileReplacements": [ 100 | { 101 | "replace": "projects/demo/src/environments/environment.ts", 102 | "with": "projects/demo/src/environments/environment.prod.ts" 103 | } 104 | ], 105 | "optimization": true, 106 | "outputHashing": "all", 107 | "sourceMap": false, 108 | "extractCss": true, 109 | "namedChunks": false, 110 | "aot": true, 111 | "extractLicenses": true, 112 | "vendorChunk": false, 113 | "buildOptimizer": true, 114 | "budgets": [ 115 | { 116 | "type": "initial", 117 | "maximumWarning": "2mb", 118 | "maximumError": "5mb" 119 | } 120 | ] 121 | } 122 | } 123 | }, 124 | "serve": { 125 | "builder": "@angular-devkit/build-angular:dev-server", 126 | "options": { 127 | "browserTarget": "demo:build", 128 | "port": 4200, 129 | "host": "0.0.0.0", 130 | "proxyConfig": "config/proxy.conf.js" 131 | }, 132 | "configurations": { 133 | "production": { 134 | "browserTarget": "demo:build:production" 135 | } 136 | } 137 | }, 138 | "extract-i18n": { 139 | "builder": "@angular-devkit/build-angular:extract-i18n", 140 | "options": { 141 | "browserTarget": "demo:build" 142 | } 143 | }, 144 | "lint": { 145 | "builder": "@angular-devkit/build-angular:tslint", 146 | "options": { 147 | "tsConfig": [ 148 | "projects/demo/tsconfig.app.json", 149 | "projects/demo/tsconfig.spec.json" 150 | ], 151 | "exclude": [ 152 | "**/node_modules/**" 153 | ] 154 | } 155 | } 156 | } 157 | }}, 158 | "defaultProject": "library" 159 | } -------------------------------------------------------------------------------- /config/cmd.ts: -------------------------------------------------------------------------------- 1 | const { spawn } = require('child_process'); 2 | const { platform } = require('process'); 3 | 4 | const proc = (cmd, args) => 5 | new Promise((resolve, reject) => { 6 | const p = createSpawn(cmd, args); 7 | p.on('exit', (code, signal) => code === 0 ? resolve() : reject(signal)); 8 | }); 9 | 10 | const npmScript = (name) => (...args) => 11 | proc('npm', ['run', name, '--', ...args]); 12 | 13 | const npmAction = (name) => (...args) => 14 | proc('npm', [name, ...args]); 15 | 16 | function createSpawn(cmd, args) { 17 | return platform === 'win32' ? spawn(cmd, args, { shell: true, stdio: 'inherit' }) : spawn(cmd, args, { stdio: 'inherit' }); 18 | } 19 | 20 | module.exports = { 21 | ng: npmScript('ng'), 22 | version: npmAction('version') 23 | }; 24 | -------------------------------------------------------------------------------- /config/dashboard.ts: -------------------------------------------------------------------------------- 1 | 2 | const util = require('gulp-util'); 3 | 4 | const colorMap = { 5 | 'development': 'yellow', 6 | 'production': 'green', 7 | 'dev': 'yellow', 8 | 'prod': 'green' 9 | }; 10 | 11 | const dash = { 12 | show: (env: string) => { 13 | const color = colorMap[env] || 'magenta'; 14 | console.log(` 15 |           _____                    _____                    _____           16 |          /\\    \\                  /\\    \\                  /\\    \\          17 |         /::\\    \\                /::\\    \\                /::\\    \\         18 |        /::::\\    \\              /::::\\    \\              /::::\\    \\        19 |       /::::::\\    \\            /::::::\\    \\            /::::::\\    \\       20 |      /::::::::\\    \\          /:::/\\:::\\    \\          /::::::::\\    \\      21 |     /::::::::::\\    \\        /:::/  \\:::\\    \\        /::::::::::\\    \\     22 |    /::::::::::::\\    \\      /:::/    \\:::\\    \\      /::::::::::::\\    \\    23 |   /::::::::::::::\\    \\    /:::/    / \\:::\\    \\    /::::::::::::::\\    \\   24 |  /:::/\\:::::::::::\\    \\  /:::/    /   \\:::\\    \\  /:::/\\:::::::::::\\    \\  25 | /:::/  \\:::::::::::\\____\\/:::/____/     \\:::\\____\\/:::/  \\:::::::::::\\____\\ 26 | \\::/    \\::::::::::/    /\\:::\\    \\      \\::/    /\\::/    \\::::::::::/    / 27 |  \\/____/ \\::::::::/    /  \\:::\\    \\      \\/____/  \\/____/ \\::::::::/    /  28 |           \\::::::/    /    \\:::\\    \\                       \\::::::/    /   29 |            \\::::/    /      \\:::\\    \\                       \\::::/    /    30 |            /:::/    /        \\:::\\    \\                      /:::/    /     31 |           /:::/    /          \\:::\\    \\                    /:::/    /      32 |          /:::/    /            \\:::\\    \\                  /:::/    /       33 |         /:::/    /              \\:::\\____\\                /:::/    /        34 |         \\::/    /                \\::/    /                \\::/    /         35 |          \\/____/                  \\/____/                  \\/____/       36 | 37 | ============ ACA Angular Application ============ 38 | Current environment: ${util.colors[color](env)} 39 | ================================================= 40 | `); 41 | } 42 | }; 43 | 44 | module.exports = { 45 | Dashboard: dash 46 | }; 47 | -------------------------------------------------------------------------------- /config/default.ts: -------------------------------------------------------------------------------- 1 | 2 | const del = require('del'); 3 | const gulp = require('gulp'); 4 | const yargs = require('yargs'); 5 | const replace = require('gulp-string-replace'); 6 | const dayjs = require('dayjs'); 7 | 8 | const { ng, version } = require('./cmd'); 9 | const { Dashboard } = require('./dashboard'); 10 | 11 | /** Node project configuration */ 12 | const npmconfig = require('../package.json'); 13 | /** Command line arguments */ 14 | const argv = yargs.argv; 15 | /** Angular CLI arguments */ 16 | const ngargs: string[] = []; 17 | 18 | const library_path = './projects/library'; 19 | 20 | if (argv.prod || (argv.demo === true && argv.prod !== 'false')) { ngargs.push('--prod'); } 21 | if (argv.aot || (argv.demo === true && argv.aot !== 'false')) { ngargs.push('--aot'); } 22 | if (argv.port && !isNaN(parseInt(argv.port, 10))) { ngargs.push(`--port=${parseInt(argv.port, 10)}`); } 23 | if (argv.ssl) { ngargs.push('--ssl'); } 24 | 25 | Dashboard.show(argv.prod ? 'prod' : 'dev'); 26 | 27 | /** Nuke old build assets */ 28 | gulp.task('clean', () => ((...globs: string[]) => del(globs))('dist/', 'compiled/', '_package')); 29 | 30 | /** Build the library */ 31 | gulp.task('ng:build', () => ng('build', 'library', ...ngargs)); 32 | 33 | /** Serve the demo with the library */ 34 | gulp.task('ng:serve', () => ng('serve', 'demo', ...ngargs)); 35 | 36 | /** Update version details to the current time and version */ 37 | gulp.task('version:update', () => { 38 | const v = npmconfig.version; 39 | const b = dayjs().startOf('s').valueOf(); 40 | return gulp.src([`${library_path}/src/lib/library.module.ts`]) // Any file globs are supported 41 | .pipe(replace(/public static version = '[0-9a-zA-Z.-]*'/g, `public static version = '${v}'`, { logs: { enabled: true } })) 42 | .pipe(replace(/readonly build = dayjs\([0-9]*\);/g, `readonly build = dayjs(${b});`, { logs: { enabled: true } })) 43 | .pipe(gulp.dest(`${library_path}/src/lib`)); 44 | }); 45 | 46 | /** Return version details back to the dev details */ 47 | gulp.task('version:clean', () => { 48 | const v = npmconfig.version; 49 | const b = dayjs().startOf('s').valueOf(); 50 | return gulp.src([`${library_path}/src/lib/library.module.ts`]) // Any file globs are supported 51 | .pipe(replace(/public static version = '[0-9a-zA-Z.-]*'/g, `public static version = 'local-dev'`, { logs: { enabled: true } })) 52 | .pipe(replace(/readonly build = dayjs\([0-9]*\);/g, `readonly build = dayjs();`, { logs: { enabled: true } })) 53 | .pipe(gulp.dest(`${library_path}/src/lib`)); 54 | }); 55 | 56 | /** Copy root project version into */ 57 | gulp.task('version:copy', () => { 58 | const v = require('../package.json').version; 59 | return gulp.src([`${library_path}/package.json`]) // Any file globs are supported 60 | .pipe(replace(/"version": "[0-9a-zA-Z.-]*"/g, `"version": "${v}"`, { logs: { enabled: true } })) 61 | .pipe(gulp.dest(library_path)); 62 | }); 63 | 64 | /** Run pre build tasks */ 65 | gulp.task('pre-build', gulp.series('version:copy', 'version:update')); 66 | 67 | /** Run post build tasks */ 68 | gulp.task('post-build', gulp.series('version:clean')); 69 | 70 | /** Run build tasks */ 71 | gulp.task('build', gulp.series('pre-build', 'ng:build', 'post-build')); 72 | 73 | /** Run serve tasks */ 74 | gulp.task('serve', gulp.series('ng:serve')); -------------------------------------------------------------------------------- /config/proxy.conf.js: -------------------------------------------------------------------------------- 1 | 2 | const domain = 'localhost:8080'; 3 | const secure = false; 4 | const valid_ssl = false; 5 | 6 | const PROXY_CONFIG = [ 7 | { 8 | context: [ 9 | "/api", 10 | "/control", 11 | "/build", 12 | "/auth", 13 | "/styles", 14 | "/scripts", 15 | "/login", 16 | "/backoffice" 17 | ], 18 | target: `http${secure ? 's' : ''}://${domain}`, 19 | secure: valid_ssl, 20 | changeOrigin: true 21 | }, 22 | { 23 | context: [ 24 | "/control/websocket" 25 | ], 26 | target: `ws${secure ? 's' : ''}://${domain}`, 27 | secure: valid_ssl, 28 | changeOrigin: true, 29 | ws: true 30 | } 31 | ]; 32 | 33 | module.exports = PROXY_CONFIG; 34 | -------------------------------------------------------------------------------- /gulpfile.ts: -------------------------------------------------------------------------------- 1 | 2 | require('./config/default'); 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ngx-composer-project", 3 | "version": "0.0.0-development", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e", 11 | "postversion": "gulp build", 12 | "publish": "gulp build && npm publish dist/library", 13 | "semantic-release": "semantic-release", 14 | "test:ci": "ng test library --watch=false --browsers=ChromeHeadless", 15 | "postpublish": "cp -r ./dist/library/package.json ./projects/library/" 16 | }, 17 | "private": true, 18 | "dependencies": { 19 | "@acaengine/ts-client": "^3.1.0", 20 | "@angular/common": "^8.0.3", 21 | "@angular/compiler": "^8.0.3", 22 | "@angular/core": "^8.0.3", 23 | "@angular/forms": "^8.0.3", 24 | "@angular/platform-browser": "^8.0.3", 25 | "@angular/platform-browser-dynamic": "^8.0.3", 26 | "@angular/router": "^8.0.3", 27 | "rxjs": "~6.4.0", 28 | "tslib": "^1.10.0", 29 | "zone.js": "~0.9.1" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "^0.800.6", 33 | "@angular-devkit/build-ng-packagr": "^0.800.6", 34 | "@angular/cli": "^8.0.6", 35 | "@angular/compiler-cli": "^8.0.3", 36 | "@angular/language-service": "^8.0.3", 37 | "@semantic-release/git": "^8.0.0", 38 | "@semantic-release/npm": "^5.3.4", 39 | "@types/jasmine": "^3.3.16", 40 | "@types/jasminewd2": "^2.0.8", 41 | "@types/node": "~8.9.4", 42 | "codelyzer": "^5.2.0", 43 | "dayjs": "^1.8.17", 44 | "gulp": "^4.0.2", 45 | "gulp-string-replace": "^1.1.2", 46 | "gulp-util": "^3.0.8", 47 | "jasmine-core": "~3.4.0", 48 | "jasmine-spec-reporter": "~4.2.1", 49 | "karma": "~4.1.0", 50 | "karma-chrome-launcher": "~2.2.0", 51 | "karma-coverage-istanbul-reporter": "^2.0.6", 52 | "karma-jasmine": "~2.0.1", 53 | "karma-jasmine-html-reporter": "^1.4.0", 54 | "ng-packagr": "^5.7.1", 55 | "protractor": "~5.4.0", 56 | "semantic-release": "^16.0.3", 57 | "ts-md5": "^1.2.7", 58 | "ts-node": "^7.0.1", 59 | "tsickle": "^0.35.0", 60 | "tslint": "~5.15.0", 61 | "typescript": "~3.4.3", 62 | "yargs": "^13.3.0" 63 | }, 64 | "repository": { 65 | "type": "git", 66 | "url": "https://github.com/acaengine/composer.git" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /projects/demo/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | {{ model.value | json }} 5 | {{ model.active }} 6 | 7 | 8 | 9 |
10 |
11 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | 2 | @import 'shared.styles'; 3 | 4 | html, body, .app { 5 | height: 100%; 6 | width: 100%; 7 | margin: 0; 8 | padding: 0; 9 | } 10 | 11 | .app { 12 | overflow: auto; 13 | } 14 | 15 | body { 16 | min-height: 100%; 17 | overflow: hidden; 18 | background-color: #eee; 19 | font-size: $desktop-font-size; 20 | @include respond-to(mobile) { 21 | font-size: $tablet-font-size; 22 | } 23 | @include respond-to(mobile) { 24 | font-size: $mobile-font-size; 25 | } 26 | } 27 | 28 | * { 29 | font-family: $main-font; 30 | } 31 | 32 | @include respond-to(not-mobile) { 33 | .mobile-only, 34 | .mobile { 35 | display: none !important; 36 | } 37 | } 38 | 39 | @include respond-to(not-tablet) { 40 | .tablet-only, 41 | .tablet { 42 | display: none !important; 43 | } 44 | } 45 | 46 | @include respond-to(not-desktop) { 47 | .desktop-only, 48 | .desktop { 49 | display: none !important; 50 | } 51 | } 52 | 53 | @include respond-to(desktop) { 54 | .not-desktop { 55 | display: none !important; 56 | } 57 | } 58 | 59 | @include respond-to(tablet) { 60 | .not-tablet, 61 | .desktop-only { 62 | display: none !important; 63 | } 64 | } 65 | 66 | @include respond-to(mobile) { 67 | .not-mobile, 68 | .desktop-only { 69 | display: none !important; 70 | } 71 | } 72 | 73 | .overlay-backdrop { 74 | background-color: rgba(#000, .54); 75 | } 76 | 77 | .test { 78 | display: flex; 79 | align-items: center; 80 | justify-content: center; 81 | flex-direction: column; 82 | margin: 1em auto; 83 | width: 24em; 84 | min-height: 10em; 85 | padding: 1em; 86 | background-color: #fff; 87 | border-radius: 4px; 88 | box-shadow: 0 1px 3px 0 rgba(#000, .2), 0 1px 1px 0 rgba(#000, .14), 0 2px 1px -1px rgba(#000, .12); 89 | 90 | } 91 | 92 | button { 93 | font-size: 1em; 94 | padding: .5em 1em; 95 | border-radius: 4px; 96 | min-width: 12em; 97 | background-color: #1E88E5; 98 | color: #fff; 99 | margin: .25em; 100 | } 101 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, ViewEncapsulation, OnInit } from '@angular/core'; 2 | 3 | import { ComposerService } from 'projects/library/src/lib/services/composer.service'; 4 | import { ACAEngineOptions, EngineZone, EngineSettings } from '@acaengine/ts-client'; 5 | 6 | import { Md5 } from 'ts-md5'; 7 | 8 | declare global { 9 | interface Window { 10 | composer: ComposerService; 11 | EngineZone: any; 12 | EngineSettings: any; 13 | debug: boolean; 14 | } 15 | } 16 | 17 | @Component({ 18 | selector: 'app-root', 19 | templateUrl: `./app.component.html`, 20 | styleUrls: ['./app.component.scss'], 21 | encapsulation: ViewEncapsulation.None 22 | }) 23 | export class AppComponent implements OnInit { 24 | public model: { [name: string]: any } = {}; 25 | 26 | constructor(private _composer: ComposerService) { } 27 | 28 | public ngOnInit(): void { 29 | window.composer = this._composer; 30 | window.EngineZone = EngineZone; 31 | window.EngineSettings = EngineSettings; 32 | window.debug = true; 33 | this.initialiseComposer(); 34 | } 35 | 36 | public initialiseComposer(tries: number = 0) { 37 | // Get domain information for configuring composer 38 | const host = location.hostname; 39 | const protocol = location.protocol; 40 | const port = location.port; 41 | const url = location.origin; 42 | // Generate configuration for composer 43 | const config: ACAEngineOptions = { 44 | scope: 'public', 45 | host: `${host}:${port}`, 46 | auth_uri: `${url}/auth/oauth/authorize`, 47 | token_uri: `${url}/auth/token`, 48 | redirect_uri: `${location.origin}/oauth-resp.html`, 49 | handle_login: true, 50 | mock: false 51 | }; 52 | localStorage.setItem(`${Md5.hashStr(config.redirect_uri)}_access_token`, 'eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJBQ0FFIiwiaWF0IjoxNTc4MDEyMDYzLCJleHAiOjE1NzkyMjE2NjMsImF1ZCI6ImxvY2FsaG9zdCIsInN1YiI6Ijg1VmJPbjE2ZWJBRnYwIiwidSI6eyJuIjoiQUNBIFN1cHBvcnQgKGxvY2FsaG9zdDo4MDgwKSIsImUiOiJzdXBwb3J0QGFjYS5pbSIsInAiOjJ9fQ.a_MdB4pUCRiwjWgoaW_7jt0aRk61piD1TuZ0HqwabrCed1BsM5vM4VD39UOhs9uKPj4eHAIM7lPbSBhjdNDtAhy_xBsoosoap5Qv9kC-ZjaEN3zcRjstgsYwFwtlLFCDBxOd5gAJVwtgXLcB8bzbzYtvl2i_UAtm5fDSL_WpUHKeGyGJEvPowDgzlBn0FqYrq6BdTOg6YotqiKMO2EhDiXDFYjIrZl4hU36gLZu_Gp5nSBCukrT0MX-nR9V4ySbBD_5pinV9PikIArNTBZLuSbhjo5ohzcljlJREuFrc38L0uoTmbitLk-qIEPYie4yrjjwkEmV-FWLVz4i_NTFM1Q'); 53 | // Setup/Initialise composer 54 | this._composer.setup(config); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | 2 | import { NgModule } from '@angular/core'; 3 | import { BrowserModule } from '@angular/platform-browser'; 4 | import { RouterModule } from '@angular/router'; 5 | 6 | import { AppComponent } from './app.component'; 7 | import { ComposerModule } from 'projects/library/src/public-api'; 8 | import { FormsModule } from '@angular/forms'; 9 | 10 | import './example-mocks'; 11 | 12 | @NgModule({ 13 | declarations: [ 14 | AppComponent 15 | ], 16 | imports: [ 17 | BrowserModule, 18 | ComposerModule, 19 | FormsModule, 20 | RouterModule.forRoot([]) 21 | ], 22 | providers: [], 23 | bootstrap: [AppComponent] 24 | }) 25 | export class AppModule { } 26 | -------------------------------------------------------------------------------- /projects/demo/src/app/example-mocks/http.mock.ts: -------------------------------------------------------------------------------- 1 | 2 | import { MockHttpRequestHandlerOptions, generateMockSystem } from '@acaengine/ts-client'; 3 | 4 | const MOCK_SYSTEMS = Array(Math.floor(Math.random() * 100 + 5)).fill(0).map(i => generateMockSystem()); 5 | 6 | const handlers: MockHttpRequestHandlerOptions[] = [ 7 | { 8 | path: '/api/engine/v1/systems', 9 | metadata: {}, 10 | method: 'GET', 11 | callback: (request) => MOCK_SYSTEMS 12 | }, 13 | { 14 | path: '/api/engine/v1/systems/:id', 15 | metadata: {}, 16 | method: 'GET', 17 | callback: (request) => { 18 | console.log('Event:', request); 19 | return MOCK_SYSTEMS.find(i => request.route_params.id === i.id); 20 | } 21 | } 22 | ] 23 | 24 | window.control.handlers = handlers; 25 | -------------------------------------------------------------------------------- /projects/demo/src/app/example-mocks/index.ts: -------------------------------------------------------------------------------- 1 | 2 | import './http.mock'; 3 | import './system.mock'; 4 | -------------------------------------------------------------------------------- /projects/demo/src/app/example-mocks/system.mock.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | const system_list = { 4 | 'sys-rhAUvsBsBo': { 5 | Bookings: [{ 6 | today: ['Yes'], 7 | $power: function () { 8 | this._system.Bookings[0].today = [...this._system.Bookings[0].today, 'No']; 9 | } 10 | }] 11 | } 12 | }; 13 | 14 | window.control.systems = system_list; 15 | -------------------------------------------------------------------------------- /projects/demo/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-dev-dragon/composer/3685019b3e890830977d09027205ebccc36fa9d9/projects/demo/src/assets/.gitkeep -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /projects/demo/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mr-dev-dragon/composer/3685019b3e890830977d09027205ebccc36fa9d9/projects/demo/src/favicon.ico -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /projects/demo/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /projects/demo/src/oauth-resp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Authentication Success 7 | 8 | 9 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /projects/demo/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /projects/demo/src/shared.styles.scss: -------------------------------------------------------------------------------- 1 | /*==============================*\ 2 | || Shared Global SASS Variables || 3 | \*==============================*/ 4 | 5 | /*=================================*\ 6 | || Color Variables || 7 | \*=================================*/ 8 | 9 | $color1: #0288D1; 10 | $color2: #FF9800; 11 | $color2-dark: #FB8C00; 12 | 13 | $color-disabled: rgba(#000, .2); 14 | 15 | $success: #4CAF50; 16 | $error: #F44336; 17 | $error-neutral: #6d6e71; 18 | $error-active: #D32F2F; 19 | $pending: #FF9800; 20 | $neutral: #6d6e71; 21 | 22 | /*=================================*\ 23 | || Font Variables || 24 | \*=================================*/ 25 | 26 | $desktop-font-size: 20px; 27 | $tablet-font-size: 20px; 28 | $mobile-font-size: 16px; 29 | 30 | $charter: 'CharterITC BT', Georgia, 'Times New Roman', Times, serif; 31 | $font-stack: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; 32 | 33 | $heading-font: $charter; 34 | $main-font: $font-stack; 35 | 36 | /*===================*\ 37 | || Media Queries || 38 | \*===================*/ 39 | 40 | $break-mobile: 450px; 41 | $break-tablet: 800px; 42 | 43 | $break-landscape-mobile: 800px; 44 | $break-landscape-tablet: 1048px; 45 | 46 | @mixin respond-to($media) { 47 | @if $media == mobile { 48 | @media only screen and (orientation: portrait) and (max-width: $break-mobile) { 49 | @content; 50 | } 51 | @media only screen and (orientation: landscape) and (max-width: $break-landscape-mobile) { 52 | @content; 53 | } 54 | } @else if $media == not-mobile { 55 | @media only screen and (orientation: portrait) and (min-width: $break-mobile + 1) { 56 | @content; 57 | } 58 | @media only screen and (orientation: landscape) and (min-width: $break-landscape-mobile + 1) { 59 | @content; 60 | } 61 | } @else if $media == tablet { 62 | @media only screen and (orientation: portrait) and (min-width: $break-mobile + 1) and (max-width: $break-tablet) { 63 | @content; 64 | } 65 | @media only screen and (orientation: landscape) and (min-width: $break-landscape-mobile + 1) and (max-width: $break-landscape-tablet) { 66 | @content; 67 | } 68 | } @else if $media == tablet-mobile { 69 | @media only screen and (orientation: portrait) and (max-width: $break-tablet) { 70 | @content; 71 | } 72 | @media only screen and (orientation: landscape) and (max-width: $break-landscape-tablet) { 73 | @content; 74 | } 75 | } @else if $media == desktop { 76 | @media only screen and (orientation: portrait) and (min-width: $break-tablet) { 77 | @content; 78 | } 79 | @media only screen and (orientation: landscape) and (min-width: $break-landscape-tablet) { 80 | @content; 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /projects/demo/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/app", 5 | "types": [] 6 | }, 7 | "include": [ 8 | "src/**/*.ts" 9 | ], 10 | "exclude": [ 11 | "src/test.ts", 12 | "src/**/*.spec.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /projects/library/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../../coverage/library'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | customLaunchers: { 30 | ChromeHeadlessNoSandbox: { 31 | base: 'ChromeHeadless', 32 | flags: ['--no-sandbox'] 33 | } 34 | }, 35 | singleRun: false, 36 | restartOnFileChange: true 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /projects/library/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/library", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | }, 7 | "whitelistedNonPeerDependencies": [ 8 | "@acaengine/ts-client" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /projects/library/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@acaengine/composer", 3 | "version": "0.0.0-development", 4 | "publishConfig": { 5 | "access": "public" 6 | }, 7 | "keywords": [ 8 | "angular", 9 | "composer" 10 | ], 11 | "description": "Engine Composer API for Angular 8+", 12 | "homepage": "https://github.com/acaengine/composer#readme", 13 | "bugs": { 14 | "url": "https://github.com/acaengine/composer/issues" 15 | }, 16 | "dependencies": { 17 | "@acaengine/ts-client": "^3.1.0" 18 | }, 19 | "peerDependencies": { 20 | "@angular/common": "^8.0.0", 21 | "@angular/core": "^8.0.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /projects/library/src/lib/directives/binding.directive.spec.ts: -------------------------------------------------------------------------------- 1 | import { BindingDirective } from './binding.directive'; 2 | 3 | describe('BindingDirective', () => { 4 | let directive: BindingDirective; 5 | let service: any; 6 | let element: any; 7 | let renderer: any; 8 | 9 | beforeEach(() => { 10 | service = jasmine.createSpyObj('ComposerService', ['bind', 'exec']); 11 | element = jasmine.createSpyObj('ElementRef', ['exec']); 12 | renderer = jasmine.createSpyObj('Renderer2', ['listen']); 13 | directive = new BindingDirective(service, element, renderer); 14 | }); 15 | 16 | it('should create an instance', () => { 17 | expect(directive).toBeTruthy(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /projects/library/src/lib/directives/binding.directive.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Directive, 3 | Input, 4 | SimpleChanges, 5 | OnInit, 6 | OnChanges, 7 | OnDestroy, 8 | ElementRef, 9 | Renderer2, 10 | EventEmitter, 11 | Output 12 | } from '@angular/core'; 13 | import { Subscription } from 'rxjs'; 14 | 15 | import { BindingService } from '../services/binding.service'; 16 | 17 | @Directive({ 18 | selector: 'i[bind], [binding], co-bind' 19 | }) 20 | export class BindingDirective implements OnInit, OnChanges, OnDestroy { 21 | /** ID of the system to bind */ 22 | @Input() public sys: string; 23 | /** Class name of the module to bind */ 24 | @Input() public mod: string; 25 | /** Index of the system to bind */ 26 | @Input() public index = 1; 27 | /** Status variable to bind to */ 28 | @Input() public bind: string; 29 | /** Method to execute */ 30 | @Input() public exec: string; 31 | /** Event to listen for on the parent */ 32 | @Input('onEvent') public on_event: string; 33 | /** ID of the system to bind to */ 34 | @Input() public params: any[] = []; 35 | /** Current value of the binding */ 36 | @Input() public model: T; 37 | /** Emitter for changes to the value of the binding */ 38 | @Output() public modelChange = new EventEmitter(); 39 | 40 | /** Listener for event on host element */ 41 | private event_listener: () => void; 42 | /** Listener for changes to the binding value */ 43 | private listener: Subscription; 44 | /** Listener for initialisation state of composer */ 45 | private init_listener: Subscription; 46 | /** Callback to unbind to the status variable */ 47 | private unbind: () => void; 48 | 49 | constructor( 50 | private _service: BindingService, 51 | private _element: ElementRef, 52 | private _renderer: Renderer2 53 | ) {} 54 | 55 | public ngOnInit(): void { 56 | this.init_listener = this._service.is_initialised.subscribe(init => { 57 | if (init) { 58 | this.bindVariable(); 59 | if (this.init_listener) { 60 | this.init_listener.unsubscribe(); 61 | this.init_listener = null; 62 | } 63 | } 64 | }); 65 | } 66 | 67 | public ngOnDestroy(): void { 68 | if (this.listener) { 69 | this.listener.unsubscribe(); 70 | this.listener = null; 71 | } 72 | if (this.unbind) { 73 | this.unbind(); 74 | this.unbind = null; 75 | } 76 | if (this.event_listener) { 77 | this.event_listener(); 78 | this.event_listener = null; 79 | } 80 | } 81 | 82 | public ngOnChanges(changes: SimpleChanges): void { 83 | if (changes.sys || changes.mod || changes.bind) { 84 | this.ngOnDestroy(); 85 | this.bindVariable(); 86 | } 87 | if (changes.on_event && this.on_event) { 88 | if (this.event_listener) { 89 | this.event_listener(); 90 | this.event_listener = null; 91 | } 92 | this.event_listener = this._renderer.listen( 93 | this._element.nativeElement, 94 | this.on_event, 95 | () => this.execute() 96 | ); 97 | } 98 | } 99 | 100 | /** Bind to set status variable */ 101 | private bindVariable() { 102 | if (this._service.initialised && this.bind && this.sys && this.mod) { 103 | const module = this._service.module(this.sys, this.mod, this.index); 104 | const binding = module.binding(this.bind); 105 | this.unbind = binding.bind(); 106 | this.listener = binding.listen(value => setTimeout(() => { 107 | this.model = value; 108 | this.modelChange.emit(this.model); 109 | }, 10)); 110 | } 111 | } 112 | 113 | /** Excute the set method on the module */ 114 | private execute() { 115 | if (this._service.initialised && this.exec && this.sys && this.mod) { 116 | const module = this._service.module(this.sys, this.mod, this.index); 117 | console.log('Params:', this.params); 118 | module.exec(this.exec, this.params).then(result => { 119 | // Emit exec result if not bound to status variable 120 | if (!this.bind) { 121 | this.model = result; 122 | this.modelChange.emit(this.model); 123 | } 124 | }); 125 | } 126 | } 127 | /** 128 | * Update local value when form control value is changed 129 | * @param value The new value for the component 130 | */ 131 | public writeValue(value: T) { 132 | this.model = value; 133 | this.modelChange.emit(this.model); 134 | if (this.exec) { 135 | this.execute(); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /projects/library/src/lib/library.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { CommonModule } from '@angular/common'; 3 | 4 | import { version } from './settings'; 5 | 6 | import { BindingDirective } from './directives/binding.directive'; 7 | 8 | import * as dayjs_api from 'dayjs'; 9 | const dayjs = dayjs_api; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | BindingDirective 14 | ], 15 | imports: [ 16 | CommonModule 17 | ], 18 | exports: [ 19 | BindingDirective 20 | ] 21 | }) 22 | export class LibraryModule { 23 | public static version = 'local-dev'; 24 | private static init = false; 25 | readonly build = dayjs(); 26 | 27 | constructor() { 28 | if (!LibraryModule.init) { 29 | const now = dayjs(); 30 | LibraryModule.init = true; 31 | const build = now.isSame(this.build, 'd') ? `Today at ${this.build.format('h:mmA')}` : this.build.format('D MMM YYYY, h:mmA'); 32 | version(LibraryModule.version, build); 33 | } 34 | } 35 | } 36 | 37 | export { LibraryModule as ACA_COMPOSER_MODULE }; 38 | export { LibraryModule as ComposerModule }; 39 | -------------------------------------------------------------------------------- /projects/library/src/lib/services/binding.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { BindingService } from './binding.service'; 4 | 5 | describe('BindingService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: BindingService = TestBed.get(BindingService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /projects/library/src/lib/services/binding.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { EngineBindingService } from '@acaengine/ts-client'; 3 | 4 | import { ComposerService } from './composer.service'; 5 | import { Observable } from 'rxjs'; 6 | 7 | @Injectable({ 8 | providedIn: 'root' 9 | }) 10 | export class BindingService extends EngineBindingService { 11 | /** Whether composer is initialised */ 12 | public _initialised: boolean = false; 13 | 14 | constructor(private composer: ComposerService) { 15 | super(undefined); 16 | this.composer.initialised.subscribe((init) => { 17 | this._initialised = init; 18 | if (init) { 19 | (this as any)._websocket = this.composer.realtime; 20 | } 21 | }); 22 | } 23 | 24 | /** Whether composer is initialised */ 25 | public get initialised(): boolean { 26 | return this._initialised; 27 | } 28 | 29 | /** Observable */ 30 | public get is_initialised(): Observable { 31 | return this.composer.initialised; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /projects/library/src/lib/services/composer.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { ComposerService } from './composer.service'; 4 | 5 | describe('ComposerService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: ComposerService = TestBed.get(ComposerService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /projects/library/src/lib/services/composer.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Observable } from 'rxjs'; 3 | import { 4 | ACAEngine, 5 | EngineAuthService, 6 | EngineBindingService, 7 | EngineWebsocket, 8 | EngineApplicationsService, 9 | EngineDriversService, 10 | EngineModulesService, 11 | EngineSystemsService, 12 | EngineZonesService, 13 | EngineUsersService, 14 | EngineHttpClient, 15 | EngineDomainsService, 16 | ACAEngineOptions, 17 | EngineTriggersService, 18 | EngineSystemTriggersService, 19 | EngineSettingsService, 20 | EngineRepositoriesService, 21 | EngineOAuthSourcesService, 22 | EngineSAMLSourcesService, 23 | EngineLDAPSourcesService 24 | } from '@acaengine/ts-client'; 25 | 26 | @Injectable({ 27 | providedIn: 'root' 28 | }) 29 | export class ComposerService { 30 | /** Initialise ACAEngine API */ 31 | public setup(options: ACAEngineOptions) { 32 | ACAEngine.init(options); 33 | } 34 | 35 | /** Observable for the intialised state of composer */ 36 | public get initialised(): Observable { 37 | return ACAEngine.initialised; 38 | } 39 | 40 | /** Observable for the intialised state of composer */ 41 | public get is_initialised(): boolean { 42 | return ACAEngine.is_initialised; 43 | } 44 | 45 | /** HTTP Client for making request with composer credentials */ 46 | public get http(): EngineHttpClient { 47 | return ACAEngine.http; 48 | } 49 | 50 | /** Authentication service for Composer */ 51 | public get auth(): EngineAuthService { 52 | return ACAEngine.auth; 53 | } 54 | 55 | /** Service for binding to engine's realtime API */ 56 | public get bindings(): EngineBindingService { 57 | return ACAEngine.bindings; 58 | } 59 | /** HTTP service for engine applications */ 60 | public get applications(): EngineApplicationsService { 61 | return ACAEngine.applications; 62 | } 63 | 64 | /** HTTP service for engine auth sources */ 65 | public get oauth_sources(): EngineOAuthSourcesService { 66 | return ACAEngine.oauth_sources; 67 | } 68 | 69 | /** HTTP service for engine auth sources */ 70 | public get saml_sources(): EngineSAMLSourcesService { 71 | return ACAEngine.saml_sources; 72 | } 73 | 74 | /** HTTP service for engine auth sources */ 75 | public get ldap_sources(): EngineLDAPSourcesService { 76 | return ACAEngine.ldap_sources; 77 | } 78 | 79 | /** HTTP service for engine domains */ 80 | public get domains(): EngineDomainsService { 81 | return ACAEngine.domains; 82 | } 83 | 84 | /** Interface for engine realtime API communications */ 85 | public get realtime(): EngineWebsocket { 86 | return ACAEngine.realtime; 87 | } 88 | 89 | /** HTTP service for engine drivers */ 90 | public get drivers(): EngineDriversService { 91 | return ACAEngine.drivers; 92 | } 93 | 94 | /** HTTP service for engine modules */ 95 | public get modules(): EngineModulesService { 96 | return ACAEngine.modules; 97 | } 98 | 99 | /** HTTP service for engine repositories */ 100 | public get repositories(): EngineRepositoriesService { 101 | return ACAEngine.repositories; 102 | } 103 | 104 | /** HTTP service for engine systems */ 105 | public get systems(): EngineSystemsService { 106 | return ACAEngine.systems; 107 | } 108 | 109 | /** HTTP service for engine triggers */ 110 | public get triggers(): EngineTriggersService { 111 | return ACAEngine.triggers; 112 | } 113 | 114 | /** HTTP service for engine system triggers */ 115 | public get system_triggers(): EngineSystemTriggersService { 116 | return ACAEngine.system_triggers; 117 | } 118 | 119 | /** HTTP service for engine auth sources */ 120 | public get users(): EngineUsersService { 121 | return ACAEngine.users; 122 | } 123 | 124 | /** HTTP service for engine auth sources */ 125 | public get settings(): EngineSettingsService { 126 | return ACAEngine.settings; 127 | } 128 | 129 | /** HTTP service for engine auth sources */ 130 | public get zones(): EngineZonesService { 131 | return ACAEngine.zones; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /projects/library/src/lib/services/http.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { EngineHttpService } from './http.service'; 4 | 5 | describe('HttpService', () => { 6 | beforeEach(() => TestBed.configureTestingModule({})); 7 | 8 | it('should be created', () => { 9 | const service: EngineHttpService = TestBed.get(EngineHttpService); 10 | expect(service).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /projects/library/src/lib/services/http.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { EngineHttpClient } from '@acaengine/ts-client'; 3 | 4 | import { ComposerService } from './composer.service'; 5 | 6 | /** 7 | * Wrapper service for the HTTP client in composer 8 | */ 9 | @Injectable({ 10 | providedIn: 'root' 11 | }) 12 | export class EngineHttpService { 13 | 14 | private get http(): EngineHttpClient { 15 | return this._composer.http; 16 | } 17 | 18 | constructor(private _composer: ComposerService) { } 19 | 20 | /** 21 | * Perform AJAX HTTP GET request 22 | * @param url URL of the GET endpoint 23 | * @param options Options to add to the request 24 | */ 25 | public get get() { 26 | return this.http.get; 27 | } 28 | 29 | /** 30 | * Perform AJAX HTTP POST request 31 | * @param url URL of the POST endpoint 32 | * @param body Body contents of the request 33 | * @param options Options to add to the request 34 | */ 35 | public get post() { 36 | return this.http.post; 37 | } 38 | 39 | /** 40 | * Perform AJAX HTTP PUT request 41 | * @param url URL of the PUT endpoint 42 | * @param body Body contents of the request 43 | * @param options Options to add to the request 44 | */ 45 | public get put() { 46 | return this.http.put; 47 | } 48 | 49 | /** 50 | * Perform AJAX HTTP PATCH request 51 | * @param url URL of the PATCH endpoint 52 | * @param body Body contents of the request 53 | * @param options Options to add to the request 54 | */ 55 | public get patch() { 56 | return this.http.patch; 57 | } 58 | 59 | /** 60 | * Perform AJAX HTTP DELETE request 61 | * @param url URL of the DELETE endpoint 62 | * @param options Options to add to the request 63 | */ 64 | public get delete() { 65 | return this.http.delete; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /projects/library/src/lib/settings.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | interface Window { 3 | debug: boolean; 4 | } 5 | } 6 | 7 | export const LIB_NAME = 'Composer'; 8 | 9 | /** 10 | * Formatted console messages for the library 11 | * @param type Identifier for the message location 12 | * @param msg Message to send 13 | * @param args Javascript variables to pass to the console 14 | * @param out Output stream name for messages. Defaults to `'debug'` 15 | * @param color Secondary colour for type 16 | */ 17 | export function log(type: string, msg: string, args?: any, out: string = 'debug', color?: string) { 18 | if (window.debug) { 19 | const clr = color ? color : '#009688'; 20 | const COLOURS = ['color: #0288D1', `color:${clr}`, 'color: default']; 21 | if (args) { 22 | if (hasColours()) { 23 | console[out](`%c[${LIB_NAME}]%c[${type}] %c${msg}`, ...COLOURS, args); 24 | } else { 25 | console[out](`[${LIB_NAME}][${type}] ${msg}`, args); 26 | } 27 | } else { 28 | if (hasColours()) { 29 | console[out](`%c[${LIB_NAME}]%c[${type}] %c${msg}`, ...COLOURS); 30 | } else { 31 | console[out](`[${LIB_NAME}][${type}] ${msg}`); 32 | } 33 | } 34 | } 35 | } 36 | 37 | /** 38 | * Log formatted error message 39 | * @param type Identifier for the message location 40 | * @param msg Message to send 41 | * @param args Javascript variables to pass to the console 42 | */ 43 | export function error(type: string, msg: string, args?: any) { 44 | log(type, msg, args, 'error'); 45 | } 46 | 47 | /** 48 | * Log formatted version information for the library 49 | * @param version 50 | * @param build 51 | * @param out 52 | */ 53 | export function version(version: string, build: string, out: any = 'debug') { 54 | const COLOURS = ['color: #f44336', `color: #9c27b0`, 'color: default']; 55 | if (hasColours()) { 56 | console[out](`%c[ACA]%c[LIB] %c${LIB_NAME} - ${version} | ${build}`, ...COLOURS); 57 | } else { 58 | console[out](`[ACA][LIB] ${LIB_NAME} - ${version} | ${build}`); 59 | } 60 | } 61 | 62 | /** 63 | * Whether the browser console supports CSS colours 64 | */ 65 | export function hasColours() { 66 | const doc = document as any; 67 | return !(doc.documentMode || /Edge/.test(navigator.userAgent)); 68 | } 69 | -------------------------------------------------------------------------------- /projects/library/src/oauth-resp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Authentication Success 7 | 8 | 9 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /projects/library/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of library 3 | */ 4 | 5 | export * from './lib/library.module'; 6 | 7 | export * from './lib/directives/binding.directive'; 8 | 9 | export * from './lib/services/composer.service'; 10 | export * from './lib/services/binding.service'; 11 | export * from './lib/services/http.service'; 12 | -------------------------------------------------------------------------------- /projects/library/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone'; 4 | import 'zone.js/dist/zone-testing'; 5 | import { getTestBed } from '@angular/core/testing'; 6 | import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; 7 | 8 | declare const require: any; 9 | 10 | // First, initialize the Angular testing environment. 11 | getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 12 | // Then we find all the tests. 13 | const context = require.context('./', true, /\.spec\.ts$/); 14 | // And load the modules. 15 | context.keys().map(context); 16 | -------------------------------------------------------------------------------- /projects/library/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/lib", 5 | "target": "es2015", 6 | "declaration": true, 7 | "inlineSources": true, 8 | "types": [], 9 | "lib": [ 10 | "dom", 11 | "es2018" 12 | ] 13 | }, 14 | "angularCompilerOptions": { 15 | "annotateForClosureCompiler": true, 16 | "skipTemplateCodegen": true, 17 | "strictMetadataEmit": true, 18 | "fullTemplateTypeCheck": true, 19 | "strictInjectionParameters": true, 20 | "enableResourceInlining": true 21 | }, 22 | "exclude": [ 23 | "src/test.ts", 24 | "**/*.spec.ts" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /projects/library/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/library/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "lib", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "lib", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ], 21 | "paths": { 22 | "library": [ 23 | "dist/library" 24 | ], 25 | "library/*": [ 26 | "dist/library/*" 27 | ] 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-declare": true, 52 | "no-var-requires": false, 53 | "object-literal-key-quotes": [ 54 | true, 55 | "as-needed" 56 | ], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [ 60 | true, 61 | "single" 62 | ], 63 | "trailing-comma": false, 64 | "component-class-suffix": true, 65 | "contextual-lifecycle": true, 66 | "directive-class-suffix": true, 67 | "no-conflicting-lifecycle": true, 68 | "no-host-metadata-property": true, 69 | "no-input-rename": true, 70 | "no-inputs-metadata-property": true, 71 | "no-output-native": true, 72 | "no-output-on-prefix": true, 73 | "no-output-rename": true, 74 | "no-outputs-metadata-property": true, 75 | "template-banana-in-box": true, 76 | "template-no-negated-async": true, 77 | "use-lifecycle-interface": true, 78 | "use-pipe-transform-interface": true 79 | } 80 | } --------------------------------------------------------------------------------