├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── angular.json ├── docs ├── 3rdpartylicenses.txt ├── assets │ ├── cat-sig.png │ └── sign-here.png ├── favicon.ico ├── index.html ├── main-LI64OOJW.js ├── polyfills-FFHMD2TL.js └── styles-5INURTSO.css ├── package-lock.json ├── package.json ├── projects ├── angular-signature-pad │ ├── .eslintrc.json │ ├── karma.conf.js │ ├── ng-package.json │ ├── package.json │ ├── src │ │ ├── lib │ │ │ ├── angular-signature-pad.component.scss │ │ │ ├── angular-signature-pad.component.spec.ts │ │ │ ├── angular-signature-pad.component.ts │ │ │ └── angular-signature-pad.module.ts │ │ ├── public-api.ts │ │ └── test.ts │ ├── tsconfig.lib.json │ ├── tsconfig.lib.prod.json │ └── tsconfig.spec.json └── demo │ ├── .eslintrc.json │ ├── public │ ├── assets │ │ ├── cat-sig.png │ │ └── sign-here.png │ └── favicon.ico │ ├── src │ ├── app │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── signature-field │ │ │ ├── signature-field.component.html │ │ │ ├── signature-field.component.scss │ │ │ └── signature-field.component.ts │ │ └── signature-view │ │ │ ├── signature-view.component.html │ │ │ ├── signature-view.component.scss │ │ │ └── signature-view.component.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── index.html │ ├── main.ts │ └── styles.scss │ ├── tsconfig.app.json │ └── tsconfig.spec.json └── tsconfig.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 = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": [ 4 | "projects/**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "tsconfig.json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "extends": [ 18 | "plugin:@angular-eslint/recommended", 19 | "plugin:@angular-eslint/template/process-inline-templates" 20 | ], 21 | "rules": { 22 | "no-underscore-dangle": "off", 23 | "semi": [ 24 | "error", 25 | "always" 26 | ], 27 | "no-console": [ 28 | "error", 29 | { 30 | "allow": [ 31 | "log", 32 | "warn", 33 | "error" 34 | ] 35 | } 36 | ], 37 | "@typescript-eslint/explicit-member-accessibility": [ 38 | "off", 39 | { 40 | "accessibility": "explicit" 41 | } 42 | ], 43 | "@angular-eslint/component-selector": "off", 44 | "@angular-eslint/directive-selector": "off", 45 | "@angular-eslint/use-lifecycle-interface": [ 46 | "error" 47 | ], 48 | "arrow-parens": [ 49 | "off", 50 | "always" 51 | ], 52 | "max-len": [ 53 | "error", 54 | { 55 | "code": 180 56 | } 57 | ] 58 | } 59 | }, 60 | { 61 | "files": [ 62 | "*.html" 63 | ], 64 | "extends": [ 65 | "plugin:@angular-eslint/template/recommended" 66 | ], 67 | "rules": {} 68 | } 69 | ] 70 | } 71 | -------------------------------------------------------------------------------- /.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 | /.angular/cache 36 | /.sass-cache 37 | /connect.lock 38 | /coverage 39 | /libpeerconnection.log 40 | npm-debug.log 41 | yarn-error.log 42 | testem.log 43 | /typings 44 | 45 | # System Files 46 | .DS_Store 47 | Thumbs.db 48 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Node generated files 2 | node_modules 3 | npm-debug.log 4 | 5 | # OS generated files 6 | Thumbs.db 7 | .DS_Store 8 | 9 | # Ignored files 10 | *.ts 11 | !*.d.ts 12 | 13 | # ngc generated files 14 | ngfactory 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | ## 6.0.0 4 | 5 | - **BREAKING** `redrawCanvas()` does not auto-clear data anymore; clear will be called internally, but still preserving data. You can return to the previous behavior by calling `clear()` after `redrawCanvas()` if you want it. 6 | - **BREAKING** `clear()` will call `redrawCanvas()` first. If you just want to clear data without redrawing, then call `clear(false)`. This behavior is to fix issues where the pad loses the style after clearing data. 7 | - **BREAKING** `clear()` will trigger `drawEnd` with the value of `null`, this can help with knowing when the pad got cleared. 8 | - Adding new `changeBackgroundColor(color: string)` function to change the background color dynamically. 9 | - New Demo UI and fixing broken showcase. 10 | - Upgrade `signature_pad` to the latest version (5.0.4) See ([Changelog](https://github.com/szimek/signature_pad/blob/master/CHANGELOG.md#504-2024-10-17)) 11 | - Support Angular 18 (Now it supports Angular >= 16; if any issues occur in the future, please report). 12 | - Update dependencies for the project 13 | 14 | ## 5.0.1 15 | 16 | - Upgrade `signature_pad` to the latest version (4.0.7) See ([Changelog](https://github.com/szimek/signature_pad/blob/master/CHANGELOG.md#407-2022-07-21)) 17 | - Support Angular 14 (Now it is supporting Angular >= 12, if any issues happened in the future, please report). 18 | - Drop support from Angular 10 & 11 as they are no more active nor LTS ([Support policy and schedule](https://angular.io/guide/releases#support-policy-and-schedule)) 19 | 20 | ## 4.0.1 21 | 22 | - Upgrade `signature_pad` to the final version 4.0.0 See ([Changelog](https://github.com/szimek/signature_pad/blob/master/CHANGELOG.md#400)) 23 | - Support Angular 13. 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Al-Mothafar Al-Hasan 4 | 5 | Copyright (c) 2016 Wulf Sölter 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular Signature Pad 2 | 3 | [![npm downloads](https://img.shields.io/npm/dt/@almothafar/angular-signature-pad.svg)](https://www.npmjs.com/package/@almothafar/angular-signature-pad) 4 | [![npm version](https://img.shields.io/npm/v/@almothafar/angular-signature-pad.svg)](https://www.npmjs.com/package/@almothafar/angular-signature-pad) 5 | [![npm license](https://img.shields.io/npm/l/@almothafar/angular-signature-pad.svg)](https://www.npmjs.com/package/@almothafar/angular-signature-pad) 6 | [![GitHub issues](https://img.shields.io/github/issues/almothafar/angular-signature-pad.svg)](https://github.com/almothafar/angular-signature-pad/issues) 7 | 8 | 9 | Angular component for [szimek/signature_pad](https://www.npmjs.com/package/signature_pad). 10 | 11 | ## Install 12 | 13 | ```shell 14 | npm install @almothafar/angular-signature-pad --save 15 | ``` 16 | 17 | ## Reference Implementation 18 | 19 | * [Live Demo](https://almothafar.github.io/angular-signature-pad/) 20 | * [Source](https://github.com/almothafar/angular-signature-pad/tree/master/projects/demo) 21 | 22 | ## Usage example 23 | 24 | API is identical to [szimek/signature_pad](https://www.npmjs.com/package/signature_pad). 25 | 26 | Options are as per [szimek/signature_pad](https://www.npmjs.com/package/signature_pad) with the following additions: 27 | 28 | * canvasWidth: width of the canvas (px) 29 | * canvasHeight: height of the canvas (px) 30 | 31 | The above options are provided to avoid accessing the DOM directly from your component to adjust the canvas size. 32 | 33 | ```typescript 34 | 35 | // import into app module 36 | 37 | import { AngularSignaturePadModule } from '@almothafar/angular-signature-pad'; 38 | 39 | ... 40 | 41 | @NgModule({ 42 | declarations: [ ], 43 | imports: [ AngularSignaturePadModule ], 44 | providers: [ ], 45 | bootstrap: [ AppComponent ] 46 | }) 47 | 48 | // then import for use in a component 49 | 50 | import { Component, ViewChild } from '@angular/core'; 51 | import { SignaturePadComponent } from '@almothafar/angular-signature-pad'; 52 | 53 | @Component({ 54 | template: '' 55 | }) 56 | 57 | export class SignaturePadPage { 58 | 59 | @ViewChild('signature') 60 | public signaturePad: SignaturePadComponent; 61 | 62 | private signaturePadOptions: NgSignaturePadOptions = { // passed through to szimek/signature_pad constructor 63 | minWidth: 5, 64 | canvasWidth: 500, 65 | canvasHeight: 300 66 | }; 67 | 68 | constructor() { 69 | // no-op 70 | } 71 | 72 | ngAfterViewInit() { 73 | // this.signaturePad is now available 74 | this.signaturePad.set('minWidth', 5); // set szimek/signature_pad options at runtime 75 | this.signaturePad.clear(); // invoke functions from szimek/signature_pad API 76 | } 77 | 78 | drawComplete(event: MouseEvent | Touch) { 79 | // will be notified of szimek/signature_pad's onEnd event 80 | console.log('Completed drawing', event); 81 | console.log(this.signaturePad.toDataURL()); 82 | } 83 | 84 | drawStart(event: MouseEvent | Touch) { 85 | // will be notified of szimek/signature_pad's onBegin event 86 | console.log('Start drawing', event); 87 | } 88 | } 89 | ``` 90 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-signature-pad": { 7 | "projectType": "library", 8 | "root": "projects/angular-signature-pad", 9 | "sourceRoot": "projects/angular-signature-pad/src", 10 | "prefix": "lib", 11 | "architect": { 12 | "build": { 13 | "builder": "@angular-devkit/build-angular:ng-packagr", 14 | "options": { 15 | "tsConfig": "projects/angular-signature-pad/tsconfig.lib.json", 16 | "project": "projects/angular-signature-pad/ng-package.json" 17 | }, 18 | "configurations": { 19 | "production": { 20 | "tsConfig": "projects/angular-signature-pad/tsconfig.lib.prod.json" 21 | } 22 | } 23 | }, 24 | "test": { 25 | "builder": "@angular-devkit/build-angular:karma", 26 | "options": { 27 | "main": "projects/angular-signature-pad/src/test.ts", 28 | "tsConfig": "projects/angular-signature-pad/tsconfig.spec.json", 29 | "karmaConfig": "projects/angular-signature-pad/karma.conf.js" 30 | } 31 | }, 32 | "lint": { 33 | "builder": "@angular-eslint/builder:lint", 34 | "options": { 35 | "lintFilePatterns": [ 36 | "projects/angular-signature-pad/**/*.ts", 37 | "projects/angular-signature-pad/**/*.html" 38 | ] 39 | } 40 | } 41 | } 42 | }, 43 | "demo": { 44 | "projectType": "application", 45 | "schematics": { 46 | "@schematics/angular:component": { 47 | "style": "scss" 48 | } 49 | }, 50 | "root": "projects/demo", 51 | "sourceRoot": "projects/demo/src", 52 | "prefix": "app", 53 | "architect": { 54 | "build": { 55 | "builder": "@angular-devkit/build-angular:application", 56 | "options": { 57 | "outputPath": { 58 | "base": "docs", 59 | "browser": "" 60 | }, 61 | "baseHref": "/angular-signature-pad/", 62 | "index": "projects/demo/src/index.html", 63 | "browser": "projects/demo/src/main.ts", 64 | "polyfills": [ 65 | "zone.js" 66 | ], 67 | "tsConfig": "projects/demo/tsconfig.app.json", 68 | "inlineStyleLanguage": "scss", 69 | "assets": [ 70 | { 71 | "glob": "**/*", 72 | "input": "projects/demo/public" 73 | } 74 | ], 75 | "styles": [ 76 | "projects/demo/src/styles.scss" 77 | ], 78 | "scripts": [] 79 | }, 80 | "configurations": { 81 | "production": { 82 | "budgets": [ 83 | { 84 | "type": "initial", 85 | "maximumWarning": "500kB", 86 | "maximumError": "1MB" 87 | }, 88 | { 89 | "type": "anyComponentStyle", 90 | "maximumWarning": "2kB", 91 | "maximumError": "4kB" 92 | } 93 | ], 94 | "outputHashing": "all" 95 | }, 96 | "development": { 97 | "optimization": false, 98 | "extractLicenses": false, 99 | "sourceMap": true 100 | } 101 | }, 102 | "defaultConfiguration": "production" 103 | }, 104 | "serve": { 105 | "builder": "@angular-devkit/build-angular:dev-server", 106 | "configurations": { 107 | "production": { 108 | "buildTarget": "demo:build:production" 109 | }, 110 | "development": { 111 | "buildTarget": "demo:build:development" 112 | } 113 | }, 114 | "defaultConfiguration": "development" 115 | }, 116 | "extract-i18n": { 117 | "builder": "@angular-devkit/build-angular:extract-i18n" 118 | }, 119 | "test": { 120 | "builder": "@angular-devkit/build-angular:karma", 121 | "options": { 122 | "polyfills": [ 123 | "zone.js", 124 | "zone.js/testing" 125 | ], 126 | "tsConfig": "projects/demo/tsconfig.spec.json", 127 | "inlineStyleLanguage": "scss", 128 | "assets": [ 129 | { 130 | "glob": "**/*", 131 | "input": "projects/demo/public" 132 | } 133 | ], 134 | "styles": [ 135 | "projects/demo/src/styles.scss" 136 | ], 137 | "scripts": [] 138 | } 139 | }, 140 | "lint": { 141 | "builder": "@angular-eslint/builder:lint", 142 | "options": { 143 | "lintFilePatterns": [ 144 | "projects/demo/**/*.ts", 145 | "projects/demo/**/*.html" 146 | ] 147 | } 148 | } 149 | } 150 | } 151 | }, 152 | "cli": { 153 | "analytics": "6e1c7da0-5e0a-4d7a-a68c-bcf04ef3cc04", 154 | "schematicCollections": [ 155 | "@angular-eslint/schematics" 156 | ] 157 | }, 158 | "schematics": { 159 | "@angular-eslint/schematics:application": { 160 | "setParserOptionsProject": true 161 | }, 162 | "@angular-eslint/schematics:library": { 163 | "setParserOptionsProject": true 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /docs/3rdpartylicenses.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- 3 | Package: @angular/core 4 | License: "MIT" 5 | 6 | The MIT License 7 | 8 | Copyright (c) 2010-2024 Google LLC. https://angular.dev/license 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | 28 | -------------------------------------------------------------------------------- 29 | Package: rxjs 30 | License: "Apache-2.0" 31 | 32 | Apache License 33 | Version 2.0, January 2004 34 | http://www.apache.org/licenses/ 35 | 36 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 37 | 38 | 1. Definitions. 39 | 40 | "License" shall mean the terms and conditions for use, reproduction, 41 | and distribution as defined by Sections 1 through 9 of this document. 42 | 43 | "Licensor" shall mean the copyright owner or entity authorized by 44 | the copyright owner that is granting the License. 45 | 46 | "Legal Entity" shall mean the union of the acting entity and all 47 | other entities that control, are controlled by, or are under common 48 | control with that entity. For the purposes of this definition, 49 | "control" means (i) the power, direct or indirect, to cause the 50 | direction or management of such entity, whether by contract or 51 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 52 | outstanding shares, or (iii) beneficial ownership of such entity. 53 | 54 | "You" (or "Your") shall mean an individual or Legal Entity 55 | exercising permissions granted by this License. 56 | 57 | "Source" form shall mean the preferred form for making modifications, 58 | including but not limited to software source code, documentation 59 | source, and configuration files. 60 | 61 | "Object" form shall mean any form resulting from mechanical 62 | transformation or translation of a Source form, including but 63 | not limited to compiled object code, generated documentation, 64 | and conversions to other media types. 65 | 66 | "Work" shall mean the work of authorship, whether in Source or 67 | Object form, made available under the License, as indicated by a 68 | copyright notice that is included in or attached to the work 69 | (an example is provided in the Appendix below). 70 | 71 | "Derivative Works" shall mean any work, whether in Source or Object 72 | form, that is based on (or derived from) the Work and for which the 73 | editorial revisions, annotations, elaborations, or other modifications 74 | represent, as a whole, an original work of authorship. For the purposes 75 | of this License, Derivative Works shall not include works that remain 76 | separable from, or merely link (or bind by name) to the interfaces of, 77 | the Work and Derivative Works thereof. 78 | 79 | "Contribution" shall mean any work of authorship, including 80 | the original version of the Work and any modifications or additions 81 | to that Work or Derivative Works thereof, that is intentionally 82 | submitted to Licensor for inclusion in the Work by the copyright owner 83 | or by an individual or Legal Entity authorized to submit on behalf of 84 | the copyright owner. For the purposes of this definition, "submitted" 85 | means any form of electronic, verbal, or written communication sent 86 | to the Licensor or its representatives, including but not limited to 87 | communication on electronic mailing lists, source code control systems, 88 | and issue tracking systems that are managed by, or on behalf of, the 89 | Licensor for the purpose of discussing and improving the Work, but 90 | excluding communication that is conspicuously marked or otherwise 91 | designated in writing by the copyright owner as "Not a Contribution." 92 | 93 | "Contributor" shall mean Licensor and any individual or Legal Entity 94 | on behalf of whom a Contribution has been received by Licensor and 95 | subsequently incorporated within the Work. 96 | 97 | 2. Grant of Copyright License. Subject to the terms and conditions of 98 | this License, each Contributor hereby grants to You a perpetual, 99 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 100 | copyright license to reproduce, prepare Derivative Works of, 101 | publicly display, publicly perform, sublicense, and distribute the 102 | Work and such Derivative Works in Source or Object form. 103 | 104 | 3. Grant of Patent License. Subject to the terms and conditions of 105 | this License, each Contributor hereby grants to You a perpetual, 106 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 107 | (except as stated in this section) patent license to make, have made, 108 | use, offer to sell, sell, import, and otherwise transfer the Work, 109 | where such license applies only to those patent claims licensable 110 | by such Contributor that are necessarily infringed by their 111 | Contribution(s) alone or by combination of their Contribution(s) 112 | with the Work to which such Contribution(s) was submitted. If You 113 | institute patent litigation against any entity (including a 114 | cross-claim or counterclaim in a lawsuit) alleging that the Work 115 | or a Contribution incorporated within the Work constitutes direct 116 | or contributory patent infringement, then any patent licenses 117 | granted to You under this License for that Work shall terminate 118 | as of the date such litigation is filed. 119 | 120 | 4. Redistribution. You may reproduce and distribute copies of the 121 | Work or Derivative Works thereof in any medium, with or without 122 | modifications, and in Source or Object form, provided that You 123 | meet the following conditions: 124 | 125 | (a) You must give any other recipients of the Work or 126 | Derivative Works a copy of this License; and 127 | 128 | (b) You must cause any modified files to carry prominent notices 129 | stating that You changed the files; and 130 | 131 | (c) You must retain, in the Source form of any Derivative Works 132 | that You distribute, all copyright, patent, trademark, and 133 | attribution notices from the Source form of the Work, 134 | excluding those notices that do not pertain to any part of 135 | the Derivative Works; and 136 | 137 | (d) If the Work includes a "NOTICE" text file as part of its 138 | distribution, then any Derivative Works that You distribute must 139 | include a readable copy of the attribution notices contained 140 | within such NOTICE file, excluding those notices that do not 141 | pertain to any part of the Derivative Works, in at least one 142 | of the following places: within a NOTICE text file distributed 143 | as part of the Derivative Works; within the Source form or 144 | documentation, if provided along with the Derivative Works; or, 145 | within a display generated by the Derivative Works, if and 146 | wherever such third-party notices normally appear. The contents 147 | of the NOTICE file are for informational purposes only and 148 | do not modify the License. You may add Your own attribution 149 | notices within Derivative Works that You distribute, alongside 150 | or as an addendum to the NOTICE text from the Work, provided 151 | that such additional attribution notices cannot be construed 152 | as modifying the License. 153 | 154 | You may add Your own copyright statement to Your modifications and 155 | may provide additional or different license terms and conditions 156 | for use, reproduction, or distribution of Your modifications, or 157 | for any such Derivative Works as a whole, provided Your use, 158 | reproduction, and distribution of the Work otherwise complies with 159 | the conditions stated in this License. 160 | 161 | 5. Submission of Contributions. Unless You explicitly state otherwise, 162 | any Contribution intentionally submitted for inclusion in the Work 163 | by You to the Licensor shall be under the terms and conditions of 164 | this License, without any additional terms or conditions. 165 | Notwithstanding the above, nothing herein shall supersede or modify 166 | the terms of any separate license agreement you may have executed 167 | with Licensor regarding such Contributions. 168 | 169 | 6. Trademarks. This License does not grant permission to use the trade 170 | names, trademarks, service marks, or product names of the Licensor, 171 | except as required for reasonable and customary use in describing the 172 | origin of the Work and reproducing the content of the NOTICE file. 173 | 174 | 7. Disclaimer of Warranty. Unless required by applicable law or 175 | agreed to in writing, Licensor provides the Work (and each 176 | Contributor provides its Contributions) on an "AS IS" BASIS, 177 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 178 | implied, including, without limitation, any warranties or conditions 179 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 180 | PARTICULAR PURPOSE. You are solely responsible for determining the 181 | appropriateness of using or redistributing the Work and assume any 182 | risks associated with Your exercise of permissions under this License. 183 | 184 | 8. Limitation of Liability. In no event and under no legal theory, 185 | whether in tort (including negligence), contract, or otherwise, 186 | unless required by applicable law (such as deliberate and grossly 187 | negligent acts) or agreed to in writing, shall any Contributor be 188 | liable to You for damages, including any direct, indirect, special, 189 | incidental, or consequential damages of any character arising as a 190 | result of this License or out of the use or inability to use the 191 | Work (including but not limited to damages for loss of goodwill, 192 | work stoppage, computer failure or malfunction, or any and all 193 | other commercial damages or losses), even if such Contributor 194 | has been advised of the possibility of such damages. 195 | 196 | 9. Accepting Warranty or Additional Liability. While redistributing 197 | the Work or Derivative Works thereof, You may choose to offer, 198 | and charge a fee for, acceptance of support, warranty, indemnity, 199 | or other liability obligations and/or rights consistent with this 200 | License. However, in accepting such obligations, You may act only 201 | on Your own behalf and on Your sole responsibility, not on behalf 202 | of any other Contributor, and only if You agree to indemnify, 203 | defend, and hold each Contributor harmless for any liability 204 | incurred by, or claims asserted against, such Contributor by reason 205 | of your accepting any such warranty or additional liability. 206 | 207 | END OF TERMS AND CONDITIONS 208 | 209 | APPENDIX: How to apply the Apache License to your work. 210 | 211 | To apply the Apache License to your work, attach the following 212 | boilerplate notice, with the fields enclosed by brackets "[]" 213 | replaced with your own identifying information. (Don't include 214 | the brackets!) The text should be enclosed in the appropriate 215 | comment syntax for the file format. We also recommend that a 216 | file or class name and description of purpose be included on the 217 | same "printed page" as the copyright notice for easier 218 | identification within third-party archives. 219 | 220 | Copyright (c) 2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors 221 | 222 | Licensed under the Apache License, Version 2.0 (the "License"); 223 | you may not use this file except in compliance with the License. 224 | You may obtain a copy of the License at 225 | 226 | http://www.apache.org/licenses/LICENSE-2.0 227 | 228 | Unless required by applicable law or agreed to in writing, software 229 | distributed under the License is distributed on an "AS IS" BASIS, 230 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 231 | See the License for the specific language governing permissions and 232 | limitations under the License. 233 | 234 | 235 | -------------------------------------------------------------------------------- 236 | Package: tslib 237 | License: "0BSD" 238 | 239 | Copyright (c) Microsoft Corporation. 240 | 241 | Permission to use, copy, modify, and/or distribute this software for any 242 | purpose with or without fee is hereby granted. 243 | 244 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 245 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 246 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 247 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 248 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 249 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 250 | PERFORMANCE OF THIS SOFTWARE. 251 | -------------------------------------------------------------------------------- 252 | Package: @angular/common 253 | License: "MIT" 254 | 255 | The MIT License 256 | 257 | Copyright (c) 2010-2024 Google LLC. https://angular.dev/license 258 | 259 | Permission is hereby granted, free of charge, to any person obtaining a copy 260 | of this software and associated documentation files (the "Software"), to deal 261 | in the Software without restriction, including without limitation the rights 262 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 263 | copies of the Software, and to permit persons to whom the Software is 264 | furnished to do so, subject to the following conditions: 265 | 266 | The above copyright notice and this permission notice shall be included in 267 | all copies or substantial portions of the Software. 268 | 269 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 270 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 271 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 272 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 273 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 274 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 275 | THE SOFTWARE. 276 | 277 | -------------------------------------------------------------------------------- 278 | Package: @angular/platform-browser 279 | License: "MIT" 280 | 281 | The MIT License 282 | 283 | Copyright (c) 2010-2024 Google LLC. https://angular.dev/license 284 | 285 | Permission is hereby granted, free of charge, to any person obtaining a copy 286 | of this software and associated documentation files (the "Software"), to deal 287 | in the Software without restriction, including without limitation the rights 288 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 289 | copies of the Software, and to permit persons to whom the Software is 290 | furnished to do so, subject to the following conditions: 291 | 292 | The above copyright notice and this permission notice shall be included in 293 | all copies or substantial portions of the Software. 294 | 295 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 296 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 297 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 298 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 299 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 300 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 301 | THE SOFTWARE. 302 | 303 | -------------------------------------------------------------------------------- 304 | Package: @angular/animations 305 | License: "MIT" 306 | 307 | The MIT License 308 | 309 | Copyright (c) 2010-2024 Google LLC. https://angular.dev/license 310 | 311 | Permission is hereby granted, free of charge, to any person obtaining a copy 312 | of this software and associated documentation files (the "Software"), to deal 313 | in the Software without restriction, including without limitation the rights 314 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 315 | copies of the Software, and to permit persons to whom the Software is 316 | furnished to do so, subject to the following conditions: 317 | 318 | The above copyright notice and this permission notice shall be included in 319 | all copies or substantial portions of the Software. 320 | 321 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 322 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 323 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 324 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 325 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 326 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 327 | THE SOFTWARE. 328 | 329 | -------------------------------------------------------------------------------- 330 | Package: @angular/forms 331 | License: "MIT" 332 | 333 | The MIT License 334 | 335 | Copyright (c) 2010-2024 Google LLC. https://angular.dev/license 336 | 337 | Permission is hereby granted, free of charge, to any person obtaining a copy 338 | of this software and associated documentation files (the "Software"), to deal 339 | in the Software without restriction, including without limitation the rights 340 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 341 | copies of the Software, and to permit persons to whom the Software is 342 | furnished to do so, subject to the following conditions: 343 | 344 | The above copyright notice and this permission notice shall be included in 345 | all copies or substantial portions of the Software. 346 | 347 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 348 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 349 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 350 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 351 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 352 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 353 | THE SOFTWARE. 354 | 355 | -------------------------------------------------------------------------------- 356 | Package: signature_pad 357 | License: "MIT" 358 | 359 | MIT License 360 | 361 | Copyright (c) 2018 Szymon Nowak 362 | 363 | Permission is hereby granted, free of charge, to any person obtaining a copy 364 | of this software and associated documentation files (the "Software"), to deal 365 | in the Software without restriction, including without limitation the rights 366 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 367 | copies of the Software, and to permit persons to whom the Software is 368 | furnished to do so, subject to the following conditions: 369 | 370 | The above copyright notice and this permission notice shall be included in all 371 | copies or substantial portions of the Software. 372 | 373 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 374 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 375 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 376 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 377 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 378 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 379 | SOFTWARE. 380 | 381 | -------------------------------------------------------------------------------- 382 | Package: zone.js 383 | License: "MIT" 384 | 385 | The MIT License 386 | 387 | Copyright (c) 2010-2024 Google LLC. https://angular.io/license 388 | 389 | Permission is hereby granted, free of charge, to any person obtaining a copy 390 | of this software and associated documentation files (the "Software"), to deal 391 | in the Software without restriction, including without limitation the rights 392 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 393 | copies of the Software, and to permit persons to whom the Software is 394 | furnished to do so, subject to the following conditions: 395 | 396 | The above copyright notice and this permission notice shall be included in 397 | all copies or substantial portions of the Software. 398 | 399 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 400 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 401 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 402 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 403 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 404 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 405 | THE SOFTWARE. 406 | 407 | -------------------------------------------------------------------------------- 408 | -------------------------------------------------------------------------------- /docs/assets/cat-sig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/docs/assets/cat-sig.png -------------------------------------------------------------------------------- /docs/assets/sign-here.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/docs/assets/sign-here.png -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/docs/favicon.ico -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SignaturepadDemo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /docs/polyfills-FFHMD2TL.js: -------------------------------------------------------------------------------- 1 | var ce=globalThis;function te(e){return(ce.__Zone_symbol_prefix||"__zone_symbol__")+e}function dt(){let e=ce.performance;function n(M){e&&e.mark&&e.mark(M)}function a(M,s){e&&e.measure&&e.measure(M,s)}n("Zone");class t{static{this.__symbol__=te}static assertZonePatched(){if(ce.Promise!==S.ZoneAwarePromise)throw new Error("Zone.js has detected that ZoneAwarePromise `(window|global).Promise` has been overwritten.\nMost likely cause is that a Promise polyfill has been loaded after Zone.js (Polyfilling Promise api is not necessary when zone.js is loaded. If you must load one, do so before loading zone.js.)")}static get root(){let s=t.current;for(;s.parent;)s=s.parent;return s}static get current(){return b.zone}static get currentTask(){return D}static __load_patch(s,i,o=!1){if(S.hasOwnProperty(s)){let g=ce[te("forceDuplicateZoneCheck")]===!0;if(!o&&g)throw Error("Already loaded patch: "+s)}else if(!ce["__Zone_disable_"+s]){let g="Zone:"+s;n(g),S[s]=i(ce,t,w),a(g,g)}}get parent(){return this._parent}get name(){return this._name}constructor(s,i){this._parent=s,this._name=i?i.name||"unnamed":"",this._properties=i&&i.properties||{},this._zoneDelegate=new f(this,this._parent&&this._parent._zoneDelegate,i)}get(s){let i=this.getZoneWith(s);if(i)return i._properties[s]}getZoneWith(s){let i=this;for(;i;){if(i._properties.hasOwnProperty(s))return i;i=i._parent}return null}fork(s){if(!s)throw new Error("ZoneSpec required!");return this._zoneDelegate.fork(this,s)}wrap(s,i){if(typeof s!="function")throw new Error("Expecting function got: "+s);let o=this._zoneDelegate.intercept(this,s,i),g=this;return function(){return g.runGuarded(o,this,arguments,i)}}run(s,i,o,g){b={parent:b,zone:this};try{return this._zoneDelegate.invoke(this,s,i,o,g)}finally{b=b.parent}}runGuarded(s,i=null,o,g){b={parent:b,zone:this};try{try{return this._zoneDelegate.invoke(this,s,i,o,g)}catch(V){if(this._zoneDelegate.handleError(this,V))throw V}}finally{b=b.parent}}runTask(s,i,o){if(s.zone!=this)throw new Error("A task can only be run in the zone of creation! (Creation: "+(s.zone||J).name+"; Execution: "+this.name+")");let g=s,{type:V,data:{isPeriodic:ee=!1,isRefreshable:Z=!1}={}}=s;if(s.state===q&&(V===z||V===y))return;let he=s.state!=A;he&&g._transitionTo(A,d);let _e=D;D=g,b={parent:b,zone:this};try{V==y&&s.data&&!ee&&!Z&&(s.cancelFn=void 0);try{return this._zoneDelegate.invokeTask(this,g,i,o)}catch(Q){if(this._zoneDelegate.handleError(this,Q))throw Q}}finally{let Q=s.state;if(Q!==q&&Q!==X)if(V==z||ee||Z&&Q===k)he&&g._transitionTo(d,A,k);else{let Ee=g._zoneDelegates;this._updateTaskCount(g,-1),he&&g._transitionTo(q,A,q),Z&&(g._zoneDelegates=Ee)}b=b.parent,D=_e}}scheduleTask(s){if(s.zone&&s.zone!==this){let o=this;for(;o;){if(o===s.zone)throw Error(`can not reschedule task to ${this.name} which is descendants of the original zone ${s.zone.name}`);o=o.parent}}s._transitionTo(k,q);let i=[];s._zoneDelegates=i,s._zone=this;try{s=this._zoneDelegate.scheduleTask(this,s)}catch(o){throw s._transitionTo(X,k,q),this._zoneDelegate.handleError(this,o),o}return s._zoneDelegates===i&&this._updateTaskCount(s,1),s.state==k&&s._transitionTo(d,k),s}scheduleMicroTask(s,i,o,g){return this.scheduleTask(new E(G,s,i,o,g,void 0))}scheduleMacroTask(s,i,o,g,V){return this.scheduleTask(new E(y,s,i,o,g,V))}scheduleEventTask(s,i,o,g,V){return this.scheduleTask(new E(z,s,i,o,g,V))}cancelTask(s){if(s.zone!=this)throw new Error("A task can only be cancelled in the zone of creation! (Creation: "+(s.zone||J).name+"; Execution: "+this.name+")");if(!(s.state!==d&&s.state!==A)){s._transitionTo(x,d,A);try{this._zoneDelegate.cancelTask(this,s)}catch(i){throw s._transitionTo(X,x),this._zoneDelegate.handleError(this,i),i}return this._updateTaskCount(s,-1),s._transitionTo(q,x),s.runCount=-1,s}}_updateTaskCount(s,i){let o=s._zoneDelegates;i==-1&&(s._zoneDelegates=null);for(let g=0;gM.hasTask(i,o),onScheduleTask:(M,s,i,o)=>M.scheduleTask(i,o),onInvokeTask:(M,s,i,o,g,V)=>M.invokeTask(i,o,g,V),onCancelTask:(M,s,i,o)=>M.cancelTask(i,o)};class f{get zone(){return this._zone}constructor(s,i,o){this._taskCounts={microTask:0,macroTask:0,eventTask:0},this._zone=s,this._parentDelegate=i,this._forkZS=o&&(o&&o.onFork?o:i._forkZS),this._forkDlgt=o&&(o.onFork?i:i._forkDlgt),this._forkCurrZone=o&&(o.onFork?this._zone:i._forkCurrZone),this._interceptZS=o&&(o.onIntercept?o:i._interceptZS),this._interceptDlgt=o&&(o.onIntercept?i:i._interceptDlgt),this._interceptCurrZone=o&&(o.onIntercept?this._zone:i._interceptCurrZone),this._invokeZS=o&&(o.onInvoke?o:i._invokeZS),this._invokeDlgt=o&&(o.onInvoke?i:i._invokeDlgt),this._invokeCurrZone=o&&(o.onInvoke?this._zone:i._invokeCurrZone),this._handleErrorZS=o&&(o.onHandleError?o:i._handleErrorZS),this._handleErrorDlgt=o&&(o.onHandleError?i:i._handleErrorDlgt),this._handleErrorCurrZone=o&&(o.onHandleError?this._zone:i._handleErrorCurrZone),this._scheduleTaskZS=o&&(o.onScheduleTask?o:i._scheduleTaskZS),this._scheduleTaskDlgt=o&&(o.onScheduleTask?i:i._scheduleTaskDlgt),this._scheduleTaskCurrZone=o&&(o.onScheduleTask?this._zone:i._scheduleTaskCurrZone),this._invokeTaskZS=o&&(o.onInvokeTask?o:i._invokeTaskZS),this._invokeTaskDlgt=o&&(o.onInvokeTask?i:i._invokeTaskDlgt),this._invokeTaskCurrZone=o&&(o.onInvokeTask?this._zone:i._invokeTaskCurrZone),this._cancelTaskZS=o&&(o.onCancelTask?o:i._cancelTaskZS),this._cancelTaskDlgt=o&&(o.onCancelTask?i:i._cancelTaskDlgt),this._cancelTaskCurrZone=o&&(o.onCancelTask?this._zone:i._cancelTaskCurrZone),this._hasTaskZS=null,this._hasTaskDlgt=null,this._hasTaskDlgtOwner=null,this._hasTaskCurrZone=null;let g=o&&o.onHasTask,V=i&&i._hasTaskZS;(g||V)&&(this._hasTaskZS=g?o:c,this._hasTaskDlgt=i,this._hasTaskDlgtOwner=this,this._hasTaskCurrZone=this._zone,o.onScheduleTask||(this._scheduleTaskZS=c,this._scheduleTaskDlgt=i,this._scheduleTaskCurrZone=this._zone),o.onInvokeTask||(this._invokeTaskZS=c,this._invokeTaskDlgt=i,this._invokeTaskCurrZone=this._zone),o.onCancelTask||(this._cancelTaskZS=c,this._cancelTaskDlgt=i,this._cancelTaskCurrZone=this._zone))}fork(s,i){return this._forkZS?this._forkZS.onFork(this._forkDlgt,this.zone,s,i):new t(s,i)}intercept(s,i,o){return this._interceptZS?this._interceptZS.onIntercept(this._interceptDlgt,this._interceptCurrZone,s,i,o):i}invoke(s,i,o,g,V){return this._invokeZS?this._invokeZS.onInvoke(this._invokeDlgt,this._invokeCurrZone,s,i,o,g,V):i.apply(o,g)}handleError(s,i){return this._handleErrorZS?this._handleErrorZS.onHandleError(this._handleErrorDlgt,this._handleErrorCurrZone,s,i):!0}scheduleTask(s,i){let o=i;if(this._scheduleTaskZS)this._hasTaskZS&&o._zoneDelegates.push(this._hasTaskDlgtOwner),o=this._scheduleTaskZS.onScheduleTask(this._scheduleTaskDlgt,this._scheduleTaskCurrZone,s,i),o||(o=i);else if(i.scheduleFn)i.scheduleFn(i);else if(i.type==G)U(i);else throw new Error("Task is missing scheduleFn.");return o}invokeTask(s,i,o,g){return this._invokeTaskZS?this._invokeTaskZS.onInvokeTask(this._invokeTaskDlgt,this._invokeTaskCurrZone,s,i,o,g):i.callback.apply(o,g)}cancelTask(s,i){let o;if(this._cancelTaskZS)o=this._cancelTaskZS.onCancelTask(this._cancelTaskDlgt,this._cancelTaskCurrZone,s,i);else{if(!i.cancelFn)throw Error("Task is not cancelable");o=i.cancelFn(i)}return o}hasTask(s,i){try{this._hasTaskZS&&this._hasTaskZS.onHasTask(this._hasTaskDlgt,this._hasTaskCurrZone,s,i)}catch(o){this.handleError(s,o)}}_updateTaskCount(s,i){let o=this._taskCounts,g=o[s],V=o[s]=g+i;if(V<0)throw new Error("More tasks executed then were scheduled.");if(g==0||V==0){let ee={microTask:o.microTask>0,macroTask:o.macroTask>0,eventTask:o.eventTask>0,change:s};this.hasTask(this._zone,ee)}}}class E{constructor(s,i,o,g,V,ee){if(this._zone=null,this.runCount=0,this._zoneDelegates=null,this._state="notScheduled",this.type=s,this.source=i,this.data=g,this.scheduleFn=V,this.cancelFn=ee,!o)throw new Error("callback is not defined");this.callback=o;let Z=this;s===z&&g&&g.useG?this.invoke=E.invokeTask:this.invoke=function(){return E.invokeTask.call(ce,Z,this,arguments)}}static invokeTask(s,i,o){s||(s=this),K++;try{return s.runCount++,s.zone.runTask(s,i,o)}finally{K==1&&$(),K--}}get zone(){return this._zone}get state(){return this._state}cancelScheduleRequest(){this._transitionTo(q,k)}_transitionTo(s,i,o){if(this._state===i||this._state===o)this._state=s,s==q&&(this._zoneDelegates=null);else throw new Error(`${this.type} '${this.source}': can not transition to '${s}', expecting state '${i}'${o?" or '"+o+"'":""}, was '${this._state}'.`)}toString(){return this.data&&typeof this.data.handleId<"u"?this.data.handleId.toString():Object.prototype.toString.call(this)}toJSON(){return{type:this.type,state:this.state,source:this.source,zone:this.zone.name,runCount:this.runCount}}}let T=te("setTimeout"),p=te("Promise"),C=te("then"),_=[],P=!1,I;function H(M){if(I||ce[p]&&(I=ce[p].resolve(0)),I){let s=I[C];s||(s=I.then),s.call(I,M)}else ce[T](M,0)}function U(M){K===0&&_.length===0&&H($),M&&_.push(M)}function $(){if(!P){for(P=!0;_.length;){let M=_;_=[];for(let s=0;sb,onUnhandledError:W,microtaskDrainDone:W,scheduleMicroTask:U,showUncaughtError:()=>!t[te("ignoreConsoleErrorUncaughtError")],patchEventTarget:()=>[],patchOnProperties:W,patchMethod:()=>W,bindArguments:()=>[],patchThen:()=>W,patchMacroTask:()=>W,patchEventPrototype:()=>W,isIEOrEdge:()=>!1,getGlobalObjects:()=>{},ObjectDefineProperty:()=>W,ObjectGetOwnPropertyDescriptor:()=>{},ObjectCreate:()=>{},ArraySlice:()=>[],patchClass:()=>W,wrapWithCurrentZone:()=>W,filterProperties:()=>[],attachOriginToPatched:()=>W,_redefineProperty:()=>W,patchCallbacks:()=>W,nativeScheduleMicroTask:H},b={parent:null,zone:new t(null,null)},D=null,K=0;function W(){}return a("Zone","Zone"),t}function _t(){let e=globalThis,n=e[te("forceDuplicateZoneCheck")]===!0;if(e.Zone&&(n||typeof e.Zone.__symbol__!="function"))throw new Error("Zone already loaded.");return e.Zone??=dt(),e.Zone}var be=Object.getOwnPropertyDescriptor,Ae=Object.defineProperty,je=Object.getPrototypeOf,Et=Object.create,Tt=Array.prototype.slice,He="addEventListener",xe="removeEventListener",Le=te(He),Ie=te(xe),ae="true",le="false",Pe=te("");function Ve(e,n){return Zone.current.wrap(e,n)}function Ge(e,n,a,t,c){return Zone.current.scheduleMacroTask(e,n,a,t,c)}var j=te,De=typeof window<"u",pe=De?window:void 0,Y=De&&pe||globalThis,gt="removeAttribute";function Fe(e,n){for(let a=e.length-1;a>=0;a--)typeof e[a]=="function"&&(e[a]=Ve(e[a],n+"_"+a));return e}function yt(e,n){let a=e.constructor.name;for(let t=0;t{let p=function(){return T.apply(this,Fe(arguments,a+"."+c))};return fe(p,T),p})(f)}}}function tt(e){return e?e.writable===!1?!1:!(typeof e.get=="function"&&typeof e.set>"u"):!0}var nt=typeof WorkerGlobalScope<"u"&&self instanceof WorkerGlobalScope,Se=!("nw"in Y)&&typeof Y.process<"u"&&Y.process.toString()==="[object process]",Be=!Se&&!nt&&!!(De&&pe.HTMLElement),rt=typeof Y.process<"u"&&Y.process.toString()==="[object process]"&&!nt&&!!(De&&pe.HTMLElement),Ce={},mt=j("enable_beforeunload"),Ye=function(e){if(e=e||Y.event,!e)return;let n=Ce[e.type];n||(n=Ce[e.type]=j("ON_PROPERTY"+e.type));let a=this||e.target||Y,t=a[n],c;if(Be&&a===pe&&e.type==="error"){let f=e;c=t&&t.call(this,f.message,f.filename,f.lineno,f.colno,f.error),c===!0&&e.preventDefault()}else c=t&&t.apply(this,arguments),e.type==="beforeunload"&&Y[mt]&&typeof c=="string"?e.returnValue=c:c!=null&&!c&&e.preventDefault();return c};function $e(e,n,a){let t=be(e,n);if(!t&&a&&be(a,n)&&(t={enumerable:!0,configurable:!0}),!t||!t.configurable)return;let c=j("on"+n+"patched");if(e.hasOwnProperty(c)&&e[c])return;delete t.writable,delete t.value;let f=t.get,E=t.set,T=n.slice(2),p=Ce[T];p||(p=Ce[T]=j("ON_PROPERTY"+T)),t.set=function(C){let _=this;if(!_&&e===Y&&(_=Y),!_)return;typeof _[p]=="function"&&_.removeEventListener(T,Ye),E&&E.call(_,null),_[p]=C,typeof C=="function"&&_.addEventListener(T,Ye,!1)},t.get=function(){let C=this;if(!C&&e===Y&&(C=Y),!C)return null;let _=C[p];if(_)return _;if(f){let P=f.call(this);if(P)return t.set.call(this,P),typeof C[gt]=="function"&&C.removeAttribute(n),P}return null},Ae(e,n,t),e[c]=!0}function ot(e,n,a){if(n)for(let t=0;tfunction(E,T){let p=a(E,T);return p.cbIdx>=0&&typeof T[p.cbIdx]=="function"?Ge(p.name,T[p.cbIdx],p,c):f.apply(E,T)})}function fe(e,n){e[j("OriginalDelegate")]=n}var Je=!1,Me=!1;function kt(){try{let e=pe.navigator.userAgent;if(e.indexOf("MSIE ")!==-1||e.indexOf("Trident/")!==-1)return!0}catch{}return!1}function vt(){if(Je)return Me;Je=!0;try{let e=pe.navigator.userAgent;(e.indexOf("MSIE ")!==-1||e.indexOf("Trident/")!==-1||e.indexOf("Edge/")!==-1)&&(Me=!0)}catch{}return Me}function Ke(e){return typeof e=="function"}function Qe(e){return typeof e=="number"}var me=!1;if(typeof window<"u")try{let e=Object.defineProperty({},"passive",{get:function(){me=!0}});window.addEventListener("test",e,e),window.removeEventListener("test",e,e)}catch{me=!1}var bt={useG:!0},ne={},st={},it=new RegExp("^"+Pe+"(\\w+)(true|false)$"),ct=j("propagationStopped");function at(e,n){let a=(n?n(e):e)+le,t=(n?n(e):e)+ae,c=Pe+a,f=Pe+t;ne[e]={},ne[e][le]=c,ne[e][ae]=f}function Pt(e,n,a,t){let c=t&&t.add||He,f=t&&t.rm||xe,E=t&&t.listeners||"eventListeners",T=t&&t.rmAll||"removeAllListeners",p=j(c),C="."+c+":",_="prependListener",P="."+_+":",I=function(k,d,A){if(k.isRemoved)return;let x=k.callback;typeof x=="object"&&x.handleEvent&&(k.callback=y=>x.handleEvent(y),k.originalDelegate=x);let X;try{k.invoke(k,d,[A])}catch(y){X=y}let G=k.options;if(G&&typeof G=="object"&&G.once){let y=k.originalDelegate?k.originalDelegate:k.callback;d[f].call(d,A.type,y,G)}return X};function H(k,d,A){if(d=d||e.event,!d)return;let x=k||d.target||e,X=x[ne[d.type][A?ae:le]];if(X){let G=[];if(X.length===1){let y=I(X[0],x,d);y&&G.push(y)}else{let y=X.slice();for(let z=0;z{throw z})}}}let U=function(k){return H(this,k,!1)},$=function(k){return H(this,k,!0)};function J(k,d){if(!k)return!1;let A=!0;d&&d.useG!==void 0&&(A=d.useG);let x=d&&d.vh,X=!0;d&&d.chkDup!==void 0&&(X=d.chkDup);let G=!1;d&&d.rt!==void 0&&(G=d.rt);let y=k;for(;y&&!y.hasOwnProperty(c);)y=je(y);if(!y&&k[c]&&(y=k),!y||y[p])return!1;let z=d&&d.eventNameToString,S={},w=y[p]=y[c],b=y[j(f)]=y[f],D=y[j(E)]=y[E],K=y[j(T)]=y[T],W;d&&d.prepend&&(W=y[j(d.prepend)]=y[d.prepend]);function M(r,u){return!me&&typeof r=="object"&&r?!!r.capture:!me||!u?r:typeof r=="boolean"?{capture:r,passive:!0}:r?typeof r=="object"&&r.passive!==!1?{...r,passive:!0}:r:{passive:!0}}let s=function(r){if(!S.isExisting)return w.call(S.target,S.eventName,S.capture?$:U,S.options)},i=function(r){if(!r.isRemoved){let u=ne[r.eventName],v;u&&(v=u[r.capture?ae:le]);let R=v&&r.target[v];if(R){for(let m=0;mre.zone.cancelTask(re);r.call(Te,"abort",ie,{once:!0}),re.removeAbortListener=()=>Te.removeEventListener("abort",ie)}if(S.target=null,ke&&(ke.taskData=null),Ue&&(S.options.once=!0),!me&&typeof re.options=="boolean"||(re.options=se),re.target=N,re.capture=Oe,re.eventName=L,B&&(re.originalDelegate=F),O?ge.unshift(re):ge.push(re),m)return N}};return y[c]=l(w,C,ee,Z,G),W&&(y[_]=l(W,P,g,Z,G,!0)),y[f]=function(){let r=this||e,u=arguments[0];d&&d.transferEventName&&(u=d.transferEventName(u));let v=arguments[2],R=v?typeof v=="boolean"?!0:v.capture:!1,m=arguments[1];if(!m)return b.apply(this,arguments);if(x&&!x(b,m,r,arguments))return;let O=ne[u],N;O&&(N=O[R?ae:le]);let L=N&&r[N];if(L)for(let F=0;Ffunction(c,f){c[ct]=!0,t&&t.apply(c,f)})}function Rt(e,n){n.patchMethod(e,"queueMicrotask",a=>function(t,c){Zone.current.scheduleMicroTask("queueMicrotask",c[0])})}var Re=j("zoneTask");function ye(e,n,a,t){let c=null,f=null;n+=t,a+=t;let E={};function T(C){let _=C.data;_.args[0]=function(){return C.invoke.apply(this,arguments)};let P=c.apply(e,_.args);return Qe(P)?_.handleId=P:(_.handle=P,_.isRefreshable=Ke(P.refresh)),C}function p(C){let{handle:_,handleId:P}=C.data;return f.call(e,_??P)}c=ue(e,n,C=>function(_,P){if(Ke(P[0])){let I={isRefreshable:!1,isPeriodic:t==="Interval",delay:t==="Timeout"||t==="Interval"?P[1]||0:void 0,args:P},H=P[0];P[0]=function(){try{return H.apply(this,arguments)}finally{let{handle:A,handleId:x,isPeriodic:X,isRefreshable:G}=I;!X&&!G&&(x?delete E[x]:A&&(A[Re]=null))}};let U=Ge(n,P[0],I,T,p);if(!U)return U;let{handleId:$,handle:J,isRefreshable:q,isPeriodic:k}=U.data;if($)E[$]=U;else if(J&&(J[Re]=U,q&&!k)){let d=J.refresh;J.refresh=function(){let{zone:A,state:x}=U;return x==="notScheduled"?(U._state="scheduled",A._updateTaskCount(U,1)):x==="running"&&(U._state="scheduling"),d.call(this)}}return J??$??U}else return C.apply(e,P)}),f=ue(e,a,C=>function(_,P){let I=P[0],H;Qe(I)?(H=E[I],delete E[I]):(H=I?.[Re],H?I[Re]=null:H=I),H?.type?H.cancelFn&&H.zone.cancelTask(H):C.apply(e,P)})}function Ct(e,n){let{isBrowser:a,isMix:t}=n.getGlobalObjects();if(!a&&!t||!e.customElements||!("customElements"in e))return;let c=["connectedCallback","disconnectedCallback","adoptedCallback","attributeChangedCallback","formAssociatedCallback","formDisabledCallback","formResetCallback","formStateRestoreCallback"];n.patchCallbacks(n,e.customElements,"customElements","define",c)}function Dt(e,n){if(Zone[n.symbol("patchEventTarget")])return;let{eventNames:a,zoneSymbolEventNames:t,TRUE_STR:c,FALSE_STR:f,ZONE_SYMBOL_PREFIX:E}=n.getGlobalObjects();for(let p=0;pf.target===e);if(!t||t.length===0)return n;let c=t[0].ignoreProperties;return n.filter(f=>c.indexOf(f)===-1)}function et(e,n,a,t){if(!e)return;let c=ut(e,n,a);ot(e,c,t)}function Ze(e){return Object.getOwnPropertyNames(e).filter(n=>n.startsWith("on")&&n.length>2).map(n=>n.substring(2))}function Ot(e,n){if(Se&&!rt||Zone[e.symbol("patchEvents")])return;let a=n.__Zone_ignore_on_properties,t=[];if(Be){let c=window;t=t.concat(["Document","SVGElement","Element","HTMLElement","HTMLBodyElement","HTMLMediaElement","HTMLFrameSetElement","HTMLFrameElement","HTMLIFrameElement","HTMLMarqueeElement","Worker"]);let f=kt()?[{target:c,ignoreProperties:["error"]}]:[];et(c,Ze(c),a&&a.concat(f),je(c))}t=t.concat(["XMLHttpRequest","XMLHttpRequestEventTarget","IDBIndex","IDBRequest","IDBOpenDBRequest","IDBDatabase","IDBTransaction","IDBCursor","WebSocket"]);for(let c=0;c{let a=n[e.__symbol__("legacyPatch")];a&&a()}),e.__load_patch("timers",n=>{let a="set",t="clear";ye(n,a,t,"Timeout"),ye(n,a,t,"Interval"),ye(n,a,t,"Immediate")}),e.__load_patch("requestAnimationFrame",n=>{ye(n,"request","cancel","AnimationFrame"),ye(n,"mozRequest","mozCancel","AnimationFrame"),ye(n,"webkitRequest","webkitCancel","AnimationFrame")}),e.__load_patch("blocking",(n,a)=>{let t=["alert","prompt","confirm"];for(let c=0;cfunction(C,_){return a.current.run(E,n,_,p)})}}),e.__load_patch("EventTarget",(n,a,t)=>{St(n,t),Dt(n,t);let c=n.XMLHttpRequestEventTarget;c&&c.prototype&&t.patchEventTarget(n,t,[c.prototype])}),e.__load_patch("MutationObserver",(n,a,t)=>{ve("MutationObserver"),ve("WebKitMutationObserver")}),e.__load_patch("IntersectionObserver",(n,a,t)=>{ve("IntersectionObserver")}),e.__load_patch("FileReader",(n,a,t)=>{ve("FileReader")}),e.__load_patch("on_property",(n,a,t)=>{Ot(t,n)}),e.__load_patch("customElements",(n,a,t)=>{Ct(n,t)}),e.__load_patch("XHR",(n,a)=>{C(n);let t=j("xhrTask"),c=j("xhrSync"),f=j("xhrListener"),E=j("xhrScheduled"),T=j("xhrURL"),p=j("xhrErrorBeforeScheduled");function C(_){let P=_.XMLHttpRequest;if(!P)return;let I=P.prototype;function H(w){return w[t]}let U=I[Le],$=I[Ie];if(!U){let w=_.XMLHttpRequestEventTarget;if(w){let b=w.prototype;U=b[Le],$=b[Ie]}}let J="readystatechange",q="scheduled";function k(w){let b=w.data,D=b.target;D[E]=!1,D[p]=!1;let K=D[f];U||(U=D[Le],$=D[Ie]),K&&$.call(D,J,K);let W=D[f]=()=>{if(D.readyState===D.DONE)if(!b.aborted&&D[E]&&w.state===q){let s=D[a.__symbol__("loadfalse")];if(D.status!==0&&s&&s.length>0){let i=w.invoke;w.invoke=function(){let o=D[a.__symbol__("loadfalse")];for(let g=0;gfunction(w,b){return w[c]=b[2]==!1,w[T]=b[1],x.apply(w,b)}),X="XMLHttpRequest.send",G=j("fetchTaskAborting"),y=j("fetchTaskScheduling"),z=ue(I,"send",()=>function(w,b){if(a.current[y]===!0||w[c])return z.apply(w,b);{let D={target:w,url:w[T],isPeriodic:!1,args:b,aborted:!1},K=Ge(X,d,D,k,A);w&&w[p]===!0&&!D.aborted&&K.state===q&&K.invoke()}}),S=ue(I,"abort",()=>function(w,b){let D=H(w);if(D&&typeof D.type=="string"){if(D.cancelFn==null||D.data&&D.data.aborted)return;D.zone.cancelTask(D)}else if(a.current[G]===!0)return S.apply(w,b)})}}),e.__load_patch("geolocation",n=>{n.navigator&&n.navigator.geolocation&&yt(n.navigator.geolocation,["getCurrentPosition","watchPosition"])}),e.__load_patch("PromiseRejectionEvent",(n,a)=>{function t(c){return function(f){lt(n,c).forEach(T=>{let p=n.PromiseRejectionEvent;if(p){let C=new p(c,{promise:f.promise,reason:f.rejection});T.invoke(C)}})}}n.PromiseRejectionEvent&&(a[j("unhandledPromiseRejectionHandler")]=t("unhandledrejection"),a[j("rejectionHandledHandler")]=t("rejectionhandled"))}),e.__load_patch("queueMicrotask",(n,a,t)=>{Rt(n,t)})}function Lt(e){e.__load_patch("ZoneAwarePromise",(n,a,t)=>{let c=Object.getOwnPropertyDescriptor,f=Object.defineProperty;function E(h){if(h&&h.toString===Object.prototype.toString){let l=h.constructor&&h.constructor.name;return(l||"")+": "+JSON.stringify(h)}return h?h.toString():Object.prototype.toString.call(h)}let T=t.symbol,p=[],C=n[T("DISABLE_WRAPPING_UNCAUGHT_PROMISE_REJECTION")]!==!1,_=T("Promise"),P=T("then"),I="__creationTrace__";t.onUnhandledError=h=>{if(t.showUncaughtError()){let l=h&&h.rejection;l?console.error("Unhandled Promise rejection:",l instanceof Error?l.message:l,"; Zone:",h.zone.name,"; Task:",h.task&&h.task.source,"; Value:",l,l instanceof Error?l.stack:void 0):console.error(h)}},t.microtaskDrainDone=()=>{for(;p.length;){let h=p.shift();try{h.zone.runGuarded(()=>{throw h.throwOriginal?h.rejection:h})}catch(l){U(l)}}};let H=T("unhandledPromiseRejectionHandler");function U(h){t.onUnhandledError(h);try{let l=a[H];typeof l=="function"&&l.call(this,h)}catch{}}function $(h){return h&&h.then}function J(h){return h}function q(h){return Z.reject(h)}let k=T("state"),d=T("value"),A=T("finally"),x=T("parentPromiseValue"),X=T("parentPromiseState"),G="Promise.then",y=null,z=!0,S=!1,w=0;function b(h,l){return r=>{try{M(h,l,r)}catch(u){M(h,!1,u)}}}let D=function(){let h=!1;return function(r){return function(){h||(h=!0,r.apply(null,arguments))}}},K="Promise resolved with itself",W=T("currentTaskTrace");function M(h,l,r){let u=D();if(h===r)throw new TypeError(K);if(h[k]===y){let v=null;try{(typeof r=="object"||typeof r=="function")&&(v=r&&r.then)}catch(R){return u(()=>{M(h,!1,R)})(),h}if(l!==S&&r instanceof Z&&r.hasOwnProperty(k)&&r.hasOwnProperty(d)&&r[k]!==y)i(r),M(h,r[k],r[d]);else if(l!==S&&typeof v=="function")try{v.call(r,u(b(h,l)),u(b(h,!1)))}catch(R){u(()=>{M(h,!1,R)})()}else{h[k]=l;let R=h[d];if(h[d]=r,h[A]===A&&l===z&&(h[k]=h[X],h[d]=h[x]),l===S&&r instanceof Error){let m=a.currentTask&&a.currentTask.data&&a.currentTask.data[I];m&&f(r,W,{configurable:!0,enumerable:!1,writable:!0,value:m})}for(let m=0;m{try{let O=h[d],N=!!r&&A===r[A];N&&(r[x]=O,r[X]=R);let L=l.run(m,void 0,N&&m!==q&&m!==J?[]:[O]);M(r,!0,L)}catch(O){M(r,!1,O)}},r)}let g="function ZoneAwarePromise() { [native code] }",V=function(){},ee=n.AggregateError;class Z{static toString(){return g}static resolve(l){return l instanceof Z?l:M(new this(null),z,l)}static reject(l){return M(new this(null),S,l)}static withResolvers(){let l={};return l.promise=new Z((r,u)=>{l.resolve=r,l.reject=u}),l}static any(l){if(!l||typeof l[Symbol.iterator]!="function")return Promise.reject(new ee([],"All promises were rejected"));let r=[],u=0;try{for(let m of l)u++,r.push(Z.resolve(m))}catch{return Promise.reject(new ee([],"All promises were rejected"))}if(u===0)return Promise.reject(new ee([],"All promises were rejected"));let v=!1,R=[];return new Z((m,O)=>{for(let N=0;N{v||(v=!0,m(L))},L=>{R.push(L),u--,u===0&&(v=!0,O(new ee(R,"All promises were rejected")))})})}static race(l){let r,u,v=new this((O,N)=>{r=O,u=N});function R(O){r(O)}function m(O){u(O)}for(let O of l)$(O)||(O=this.resolve(O)),O.then(R,m);return v}static all(l){return Z.allWithCallback(l)}static allSettled(l){return(this&&this.prototype instanceof Z?this:Z).allWithCallback(l,{thenCallback:u=>({status:"fulfilled",value:u}),errorCallback:u=>({status:"rejected",reason:u})})}static allWithCallback(l,r){let u,v,R=new this((L,F)=>{u=L,v=F}),m=2,O=0,N=[];for(let L of l){$(L)||(L=this.resolve(L));let F=O;try{L.then(B=>{N[F]=r?r.thenCallback(B):B,m--,m===0&&u(N)},B=>{r?(N[F]=r.errorCallback(B),m--,m===0&&u(N)):v(B)})}catch(B){v(B)}m++,O++}return m-=2,m===0&&u(N),R}constructor(l){let r=this;if(!(r instanceof Z))throw new Error("Must be an instanceof Promise.");r[k]=y,r[d]=[];try{let u=D();l&&l(u(b(r,z)),u(b(r,S)))}catch(u){M(r,!1,u)}}get[Symbol.toStringTag](){return"Promise"}get[Symbol.species](){return Z}then(l,r){let u=this.constructor?.[Symbol.species];(!u||typeof u!="function")&&(u=this.constructor||Z);let v=new u(V),R=a.current;return this[k]==y?this[d].push(R,v,l,r):o(this,R,v,l,r),v}catch(l){return this.then(null,l)}finally(l){let r=this.constructor?.[Symbol.species];(!r||typeof r!="function")&&(r=Z);let u=new r(V);u[A]=A;let v=a.current;return this[k]==y?this[d].push(v,u,l,l):o(this,v,u,l,l),u}}Z.resolve=Z.resolve,Z.reject=Z.reject,Z.race=Z.race,Z.all=Z.all;let he=n[_]=n.Promise;n.Promise=Z;let _e=T("thenPatched");function Q(h){let l=h.prototype,r=c(l,"then");if(r&&(r.writable===!1||!r.configurable))return;let u=l.then;l[P]=u,h.prototype.then=function(v,R){return new Z((O,N)=>{u.call(this,O,N)}).then(v,R)},h[_e]=!0}t.patchThen=Q;function Ee(h){return function(l,r){let u=h.apply(l,r);if(u instanceof Z)return u;let v=u.constructor;return v[_e]||Q(v),u}}return he&&(Q(he),ue(n,"fetch",h=>Ee(h))),Promise[a.__symbol__("uncaughtPromiseErrors")]=p,Z})}function It(e){e.__load_patch("toString",n=>{let a=Function.prototype.toString,t=j("OriginalDelegate"),c=j("Promise"),f=j("Error"),E=function(){if(typeof this=="function"){let _=this[t];if(_)return typeof _=="function"?a.call(_):Object.prototype.toString.call(_);if(this===Promise){let P=n[c];if(P)return a.call(P)}if(this===Error){let P=n[f];if(P)return a.call(P)}}return a.call(this)};E[t]=a,Function.prototype.toString=E;let T=Object.prototype.toString,p="[object Promise]";Object.prototype.toString=function(){return typeof Promise=="function"&&this instanceof Promise?p:T.call(this)}})}function Mt(e,n,a,t,c){let f=Zone.__symbol__(t);if(n[f])return;let E=n[f]=n[t];n[t]=function(T,p,C){return p&&p.prototype&&c.forEach(function(_){let P=`${a}.${t}::`+_,I=p.prototype;try{if(I.hasOwnProperty(_)){let H=e.ObjectGetOwnPropertyDescriptor(I,_);H&&H.value?(H.value=e.wrapWithCurrentZone(H.value,P),e._redefineProperty(p.prototype,_,H)):I[_]&&(I[_]=e.wrapWithCurrentZone(I[_],P))}else I[_]&&(I[_]=e.wrapWithCurrentZone(I[_],P))}catch{}}),E.call(n,T,p,C)},e.attachOriginToPatched(n[t],E)}function Zt(e){e.__load_patch("util",(n,a,t)=>{let c=Ze(n);t.patchOnProperties=ot,t.patchMethod=ue,t.bindArguments=Fe,t.patchMacroTask=pt;let f=a.__symbol__("BLACK_LISTED_EVENTS"),E=a.__symbol__("UNPATCHED_EVENTS");n[E]&&(n[f]=n[E]),n[f]&&(a[f]=a[E]=n[f]),t.patchEventPrototype=wt,t.patchEventTarget=Pt,t.isIEOrEdge=vt,t.ObjectDefineProperty=Ae,t.ObjectGetOwnPropertyDescriptor=be,t.ObjectCreate=Et,t.ArraySlice=Tt,t.patchClass=ve,t.wrapWithCurrentZone=Ve,t.filterProperties=ut,t.attachOriginToPatched=fe,t._redefineProperty=Object.defineProperty,t.patchCallbacks=Mt,t.getGlobalObjects=()=>({globalSources:st,zoneSymbolEventNames:ne,eventNames:c,isBrowser:Be,isMix:rt,isNode:Se,TRUE_STR:ae,FALSE_STR:le,ZONE_SYMBOL_PREFIX:Pe,ADD_EVENT_LISTENER_STR:He,REMOVE_EVENT_LISTENER_STR:xe})})}function At(e){Lt(e),It(e),Zt(e)}var ft=_t();At(ft);Nt(ft); 3 | -------------------------------------------------------------------------------- /docs/styles-5INURTSO.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/docs/styles-5INURTSO.css -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@almothafar/angular-signature-pad", 3 | "version": "6.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng build angular-signature-pad --watch", 7 | "start:demo": "ng serve demo", 8 | "build": "ng build", 9 | "build:prod": "rimraf dist && ng build angular-signature-pad --configuration production && npm run copyExtra", 10 | "build:docs": "rimraf demo && ng build demo --configuration production", 11 | "pack": "cd dist/ && npm pack", 12 | "publish": "cd dist/ && npm publish --access=public", 13 | "test": "ng test", 14 | "lint": "ng lint", 15 | "copyExtra": "node -e \"require('fs').copyFileSync('README.md', './dist/README.md');require('fs').copyFileSync('CHANGELOG.md', './dist/CHANGELOG.md');\"" 16 | }, 17 | "dependencies": { 18 | "@angular/animations": "^18.2.10", 19 | "@angular/common": "^18.2.10", 20 | "@angular/compiler": "^18.2.10", 21 | "@angular/core": "^18.2.10", 22 | "@angular/forms": "^18.2.10", 23 | "@angular/platform-browser": "^18.2.10", 24 | "@angular/platform-browser-dynamic": "^18.2.10", 25 | "@angular/router": "^18.2.10", 26 | "rxjs": "~7.8.1", 27 | "signature_pad": "^5.0.4", 28 | "tslib": "^2.8.1", 29 | "zone.js": "~0.14.10" 30 | }, 31 | "devDependencies": { 32 | "@angular-devkit/build-angular": "^18.2.11", 33 | "@angular-eslint/builder": "^18.4.0", 34 | "@angular-eslint/eslint-plugin": "^18.4.0", 35 | "@angular-eslint/eslint-plugin-template": "^18.4.0", 36 | "@angular-eslint/schematics": "^18.4.0", 37 | "@angular-eslint/template-parser": "^18.4.0", 38 | "@angular/cli": "^18.2.11", 39 | "@angular/compiler-cli": "^18.2.10", 40 | "@types/jasmine": "~5.1.4", 41 | "@types/node": "^22.8.6", 42 | "@typescript-eslint/eslint-plugin": "^8.12.2", 43 | "@typescript-eslint/parser": "^8.12.2", 44 | "eslint": "^8.57.1", 45 | "eslint-plugin-import": "latest", 46 | "eslint-plugin-jsdoc": "latest", 47 | "eslint-plugin-prefer-arrow": "latest", 48 | "jasmine-core": "~5.4.0", 49 | "jasmine-spec-reporter": "~7.0.0", 50 | "karma": "~6.4.4", 51 | "karma-chrome-launcher": "~3.2.0", 52 | "karma-coverage": "~2.2.1", 53 | "karma-jasmine": "~5.1.0", 54 | "karma-jasmine-html-reporter": "^2.1.0", 55 | "ng-packagr": "^18.2.1", 56 | "rimraf": "~6.0.1", 57 | "typescript": "~5.5.4" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "projects/angular-signature-pad/tsconfig.lib.json", 14 | "projects/angular-signature-pad/tsconfig.spec.json" 15 | ], 16 | "createDefaultProgram": true 17 | }, 18 | "rules": { 19 | "@angular-eslint/directive-selector": [ 20 | "error", 21 | { 22 | "type": "attribute", 23 | "prefix": "lib", 24 | "style": "camelCase" 25 | } 26 | ] 27 | } 28 | }, 29 | { 30 | "files": [ 31 | "*.html" 32 | ], 33 | "rules": {} 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/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'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | jasmineHtmlReporter: { 19 | suppressAll: true // removes the duplicated traces 20 | }, 21 | coverageReporter: { 22 | dir: require('path').join(__dirname, '../../coverage/angular-signature-pad'), 23 | subdir: '.', 24 | reporters: [ 25 | {type: 'html'}, 26 | {type: 'text-summary'} 27 | ] 28 | }, 29 | reporters: ['progress', 'kjhtml'], 30 | port: 9876, 31 | colors: true, 32 | logLevel: config.LOG_INFO, 33 | autoWatch: true, 34 | browsers: ['Chrome'], 35 | singleRun: false, 36 | restartOnFileChange: true 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/ng-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", 3 | "dest": "../../dist/", 4 | "lib": { 5 | "entryFile": "src/public-api.ts" 6 | }, 7 | "allowedNonPeerDependencies": [ 8 | "signature_pad" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@almothafar/angular-signature-pad", 3 | "version": "6.0.0", 4 | "scripts": { 5 | "build": "ng-packagr -p package.json", 6 | "build:prod": "rimraf dist && ng build angular-signature-pad --prod" 7 | }, 8 | "description": "Angular Component wrapper for szimek/signature_pad", 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/almothafar/angular-signature-pad.git" 12 | }, 13 | "keywords": [ 14 | "signature", 15 | "sign", 16 | "finger", 17 | "canvas" 18 | ], 19 | "author": "Al-Mothafar Al-Hasan (https://almothafar.com)", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/almothafar/angular-signature-pad/issues" 23 | }, 24 | "homepage": "https://github.com/almothafar/angular-signature-pad#readme", 25 | "peerDependencies": { 26 | "@angular/common": ">=16.0.0", 27 | "@angular/core": ">=16.0.0" 28 | }, 29 | "dependencies": { 30 | "signature_pad": "^5.0.4", 31 | "tslib": "^2.8.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/src/lib/angular-signature-pad.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | // This should be solving issue with open area that don't have signature pad canvas 3 | background: antiquewhite; 4 | display: flex; 5 | align-items: center; 6 | justify-content: center; 7 | 8 | .signature-pad-canvas { 9 | border: 1px solid gray; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/src/lib/angular-signature-pad.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { ComponentFixture, TestBed } from '@angular/core/testing'; 2 | 3 | import { SignaturePadComponent } from './angular-signature-pad.component'; 4 | 5 | describe('AngularSignaturePadComponent', () => { 6 | let component: SignaturePadComponent; 7 | let fixture: ComponentFixture; 8 | 9 | beforeEach(async () => { 10 | await TestBed.configureTestingModule({ 11 | declarations: [SignaturePadComponent], 12 | }).compileComponents(); 13 | }); 14 | 15 | beforeEach(() => { 16 | fixture = TestBed.createComponent(SignaturePadComponent); 17 | component = fixture.componentInstance; 18 | fixture.detectChanges(); 19 | }); 20 | 21 | it('should create', () => { 22 | expect(component).toBeTruthy(); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/src/lib/angular-signature-pad.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterContentInit, Component, ElementRef, EventEmitter, Input, OnDestroy, Output} from '@angular/core'; 2 | import SignaturePad, {Options, PointGroup} from 'signature_pad'; 3 | 4 | export interface NgSignaturePadOptions extends Options { 5 | canvasBackground?: string; 6 | canvasHeight?: number; 7 | canvasWidth?: number; 8 | } 9 | 10 | @Component({ 11 | template: '', 12 | selector: 'signature-pad', 13 | styleUrls: ['./angular-signature-pad.component.scss'], 14 | }) 15 | export class SignaturePadComponent implements AfterContentInit, OnDestroy { 16 | @Input() public options: NgSignaturePadOptions; 17 | 18 | @Output() public drawStart: EventEmitter; 19 | @Output() public drawBeforeUpdate: EventEmitter; 20 | @Output() public drawAfterUpdate: EventEmitter; 21 | @Output() public drawEnd: EventEmitter; 22 | 23 | private signaturePad: SignaturePad; 24 | private extraWidth: number; 25 | 26 | constructor(private _elementRef: ElementRef) { 27 | this.options = this.options || {} as NgSignaturePadOptions; 28 | this.drawStart = new EventEmitter(); 29 | this.drawBeforeUpdate = new EventEmitter(); 30 | this.drawAfterUpdate = new EventEmitter(); 31 | this.drawEnd = new EventEmitter(); 32 | } 33 | 34 | public ngAfterContentInit(): void { 35 | const canvas: HTMLCanvasElement = this.initCanvas(this.options); 36 | this.initSignaturePad(canvas, this.options); 37 | this.redrawCanvas(); 38 | } 39 | 40 | public ngOnDestroy(): void { 41 | const canvas: HTMLCanvasElement = this.getCanvas(); 42 | canvas.width = 0; 43 | canvas.height = 0; 44 | } 45 | 46 | // noinspection JSUnusedGlobalSymbols 47 | public getSignaturePad(): SignaturePad { 48 | return this.signaturePad; 49 | } 50 | 51 | public getCanvas(): HTMLCanvasElement { 52 | return this._elementRef.nativeElement.querySelector('canvas'); 53 | } 54 | 55 | // noinspection JSUnusedGlobalSymbols 56 | /** 57 | * Redraw or Resize canvas, note this will clear data. 58 | */ 59 | public redrawCanvas(): void { 60 | const canvas: HTMLCanvasElement = this.getCanvas(); 61 | // when zoomed out to less than 100%, for some very strange reason, 62 | // some browsers report devicePixelRatio as less than 1, and only part of the canvas is cleared then. 63 | const ratio: number = Math.max(window.devicePixelRatio || 1, 1); 64 | canvas.width = this._getWidthFix(canvas) * ratio; 65 | canvas.height = this._getHeightFix(canvas) * ratio; 66 | canvas.getContext('2d').scale(ratio, ratio); 67 | this.changeBackgroundColor(this.signaturePad.backgroundColor); 68 | } 69 | 70 | // noinspection JSUnusedGlobalSymbols 71 | /** 72 | * Change the color of the background dynamically. 73 | */ 74 | public changeBackgroundColor(color: string): void { 75 | this.signaturePad.backgroundColor = color; 76 | const data = this.signaturePad.toData(); 77 | this.signaturePad.clear(); 78 | this.signaturePad.fromData(data); 79 | } 80 | 81 | // noinspection JSUnusedGlobalSymbols 82 | /** 83 | * Returns signature image as an array of point groups 84 | */ 85 | public toData(): PointGroup[] { 86 | if (this.signaturePad) { 87 | return this.signaturePad.toData(); 88 | } else { 89 | return []; 90 | } 91 | } 92 | 93 | // noinspection JSUnusedGlobalSymbols 94 | /** 95 | * Draws signature image from an array of point groups 96 | */ 97 | public fromData(points: Array): void { 98 | this.signaturePad.fromData(points); 99 | } 100 | 101 | // noinspection JSUnusedGlobalSymbols 102 | /** 103 | * Returns signature image as data URL (see https://mdn.io/todataurl for the list of possible parameters) 104 | */ 105 | public toDataURL(imageType?: string, quality?: number): string { 106 | return this.signaturePad.toDataURL(imageType, quality); // save image as data URL 107 | } 108 | 109 | // noinspection JSUnusedGlobalSymbols 110 | /** 111 | * Draws signature image from data URL 112 | */ 113 | public fromDataURL(dataURL: string, options: { ratio?: number; width?: number; height?: number } = {}): Promise { 114 | // set default height and width on read data from URL 115 | if (!options.hasOwnProperty('height') && this.options.canvasHeight) { 116 | options.height = this.options.canvasHeight; 117 | } 118 | if (!options.hasOwnProperty('width') && this.options.canvasWidth) { 119 | options.width = this.options.canvasWidth; 120 | } 121 | return this.signaturePad.fromDataURL(dataURL, options); 122 | } 123 | 124 | // noinspection JSUnusedGlobalSymbols 125 | /** 126 | * Clears the canvas 127 | */ 128 | public clear(redraw: boolean = true): void { 129 | if (redraw) { 130 | this.signaturePad.clear(); 131 | this.redrawCanvas(); 132 | } else { 133 | this.signaturePad.clear(); 134 | } 135 | this.endStroke(null); 136 | } 137 | 138 | // noinspection JSUnusedGlobalSymbols 139 | /** 140 | * Returns true if canvas is empty, otherwise returns false 141 | */ 142 | public isEmpty(): boolean { 143 | return this.signaturePad.isEmpty(); 144 | } 145 | 146 | // noinspection JSUnusedGlobalSymbols 147 | /** 148 | * Unbinds all event handlers 149 | */ 150 | public off(): void { 151 | this.signaturePad.off(); 152 | } 153 | 154 | /** 155 | * Rebinds all event handlers 156 | */ 157 | public on(): void { 158 | this.signaturePad.on(); 159 | } 160 | 161 | /** 162 | * set an option on the signaturePad - e.g. set('minWidth', 50); 163 | * @param option one of SignaturePad to set with value, properties of NgSignaturePadOptions 164 | * @param value the value of option 165 | */ 166 | public set(option: string, value: any): void { 167 | if (option === 'canvasHeight' || option === 'canvasWidth') { 168 | const canvas: HTMLCanvasElement = this.getCanvas(); 169 | const canvasOption = option.replace('canvas', '').toLowerCase(); 170 | if (canvas[canvasOption] === value) { 171 | // Same value, no need to change and redraw 172 | return; 173 | } 174 | canvas[canvasOption] = value - this.extraWidth; 175 | this.clear(); 176 | } else { 177 | if (this.signaturePad[option] === value) { 178 | // Same value, no need to change and redraw 179 | return; 180 | } 181 | this.signaturePad[option] = value; 182 | } 183 | } 184 | 185 | /** 186 | * notify subscribers on signature begin 187 | */ 188 | public beginStroke(event: MouseEvent | Touch): void { 189 | this.drawStart.emit(event); 190 | } 191 | 192 | public beforeUpdateStroke(event: MouseEvent | Touch): void { 193 | this.drawBeforeUpdate.emit(event); 194 | } 195 | 196 | public afterUpdateStroke(event: MouseEvent | Touch): void { 197 | this.drawAfterUpdate.emit(event); 198 | } 199 | 200 | /** 201 | * notify subscribers on signature end 202 | */ 203 | public endStroke(event: MouseEvent | Touch): void { 204 | this.drawEnd.emit(event); 205 | } 206 | 207 | private initCanvas(options: NgSignaturePadOptions): HTMLCanvasElement { 208 | const canvas: HTMLCanvasElement = this.getCanvas(); 209 | if (this.options.canvasHeight) { 210 | canvas.height = options.canvasHeight - 2; 211 | } 212 | if (this.options.canvasWidth) { 213 | canvas.width = options.canvasWidth - 2; 214 | } 215 | if (this.options.canvasBackground) { 216 | canvas.style.background = options.canvasBackground; 217 | } 218 | return canvas; 219 | } 220 | 221 | private initSignaturePad(canvas: HTMLCanvasElement, options?: Options): void { 222 | this.signaturePad = new SignaturePad(canvas, options); 223 | this.signaturePad.addEventListener('beginStroke', (event: CustomEvent) => this.beginStroke(event.detail)); 224 | this.signaturePad.addEventListener('beforeUpdateStroke', (event: CustomEvent) => this.beforeUpdateStroke(event.detail)); 225 | this.signaturePad.addEventListener('afterUpdateStroke', (event: CustomEvent) => this.afterUpdateStroke(event.detail)); 226 | this.signaturePad.addEventListener('endStroke', (event: CustomEvent) => this.endStroke(event.detail)); 227 | } 228 | 229 | /** 230 | * To prevent the growing effect when the redrawCanvas is called for the width 231 | * @param canvas 232 | * @private 233 | */ 234 | private _getWidthFix(canvas: HTMLCanvasElement) { 235 | const computedStyle = getComputedStyle(canvas); 236 | 237 | const extraPadding = parseFloat(computedStyle.paddingLeft) + parseFloat(computedStyle.paddingRight); 238 | const extraBorder = parseFloat(computedStyle.borderLeftWidth) + parseFloat(computedStyle.borderRightWidth); 239 | this.extraWidth = extraPadding + extraBorder; 240 | return canvas.offsetWidth - (extraPadding + extraBorder); 241 | } 242 | 243 | /** 244 | * To prevent the growing effect when the redrawCanvas is called for the height 245 | * @param canvas 246 | * @private 247 | */ 248 | private _getHeightFix(canvas: HTMLCanvasElement) { 249 | const computedStyle = getComputedStyle(canvas); 250 | 251 | const extraPadding = parseFloat(computedStyle.paddingTop) + parseFloat(computedStyle.paddingBottom); 252 | const extraBorder = parseFloat(computedStyle.borderTopWidth) + parseFloat(computedStyle.borderBottomWidth); 253 | 254 | return canvas.offsetHeight - (extraPadding + extraBorder); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/src/lib/angular-signature-pad.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { SignaturePadComponent } from './angular-signature-pad.component'; 3 | 4 | @NgModule({ 5 | declarations: [SignaturePadComponent], 6 | imports: [], 7 | exports: [SignaturePadComponent], 8 | }) 9 | export class AngularSignaturePadModule { 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/src/public-api.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Public API Surface of angular-signature-pad 3 | */ 4 | 5 | export * from './lib/angular-signature-pad.component'; 6 | export * from './lib/angular-signature-pad.module'; 7 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/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 | // First, initialize the Angular testing environment. 9 | getTestBed().initTestEnvironment( 10 | BrowserDynamicTestingModule, 11 | platformBrowserDynamicTesting(), { 12 | teardown: { destroyAfterEach: false } 13 | } 14 | ); 15 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/lib", 6 | "declaration": true, 7 | "declarationMap": true, 8 | "inlineSources": true, 9 | "types": [], 10 | "lib": [ 11 | "DOM", 12 | "ESNext" 13 | ] 14 | }, 15 | "angularCompilerOptions": { 16 | "skipTemplateCodegen": true, 17 | "strictMetadataEmit": true, 18 | "enableResourceInlining": true 19 | }, 20 | "exclude": [ 21 | "src/test.ts", 22 | "**/*.spec.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/tsconfig.lib.prod.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.lib.json", 4 | "compilerOptions": { 5 | "declarationMap": false 6 | }, 7 | "angularCompilerOptions": { 8 | "compilationMode": "partial" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /projects/angular-signature-pad/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../../out-tsc/spec", 6 | "types": [ 7 | "jasmine" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts" 12 | ], 13 | "include": [ 14 | "**/*.spec.ts", 15 | "**/*.d.ts" 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /projects/demo/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../.eslintrc.json", 3 | "ignorePatterns": [ 4 | "!**/*" 5 | ], 6 | "overrides": [ 7 | { 8 | "files": [ 9 | "*.ts" 10 | ], 11 | "parserOptions": { 12 | "project": [ 13 | "projects/demo/tsconfig.(app|spec).json" 14 | ], 15 | "createDefaultProgram": true 16 | }, 17 | "rules": { 18 | "@angular-eslint/directive-selector": [ 19 | "error", 20 | { 21 | "type": "attribute", 22 | "prefix": "app", 23 | "style": "camelCase" 24 | } 25 | ], 26 | "@angular-eslint/component-selector": [ 27 | "error", 28 | { 29 | "type": "element", 30 | "prefix": "app", 31 | "style": "kebab-case" 32 | } 33 | ] 34 | } 35 | }, 36 | { 37 | "files": [ 38 | "*.html" 39 | ], 40 | "rules": {} 41 | } 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /projects/demo/public/assets/cat-sig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/projects/demo/public/assets/cat-sig.png -------------------------------------------------------------------------------- /projects/demo/public/assets/sign-here.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/projects/demo/public/assets/sign-here.png -------------------------------------------------------------------------------- /projects/demo/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/almothafar/angular-signature-pad/b0b76f3ac8ab5107ec243a688031a7394c8dab62/projects/demo/public/favicon.ico -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |

2 | @almothafar/angular-signature-pad demo 3 |

4 | 5 |
6 |
7 | 8 | 9 |
10 | @for (item of items; track item; let idx = $index) { 11 |
12 |

{{ titles[idx] }}

13 |
14 | 15 | 16 |
17 |
18 |
19 | 21 |
22 |
23 | 24 |
25 |
26 |
27 | } 28 |
29 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | $base-width: 850px; 2 | 3 | form { 4 | padding-top: 20px; 5 | margin: 0 auto; 6 | max-width: $base-width + 100; 7 | 8 | h3 { 9 | margin: 10px 5px 0; 10 | } 11 | 12 | button { 13 | margin: 5px; 14 | } 15 | } 16 | 17 | .sig-container { 18 | margin: 10px 0; 19 | border: dashed 1px black; 20 | max-width: $base-width; 21 | 22 | .row { 23 | display: flex; 24 | flex-direction: row; 25 | flex-wrap: nowrap; 26 | justify-content: space-between; 27 | align-items: center; 28 | 29 | .col { 30 | flex-basis: calc(50% - 40px); 31 | flex-grow: 1; 32 | flex-shrink: 1; 33 | padding: 0 10px; 34 | } 35 | } 36 | 37 | app-signature-field { 38 | margin: 0 auto; 39 | max-width: $base-width - 50; 40 | display: block; 41 | } 42 | } 43 | 44 | .sig-container-image { 45 | height: 400px; 46 | width: $base-width - 50; 47 | } 48 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { AppComponent } from './app.component'; 3 | 4 | describe('AppComponent', () => { 5 | beforeEach(async () => { 6 | await TestBed.configureTestingModule({ 7 | imports: [AppComponent], 8 | }).compileComponents(); 9 | }); 10 | 11 | it('should create the app', () => { 12 | const fixture = TestBed.createComponent(AppComponent); 13 | const app = fixture.componentInstance; 14 | expect(app).toBeTruthy(); 15 | }); 16 | 17 | it(`should have the 'demo' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('demo'); 21 | }); 22 | 23 | it('should render title', () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | fixture.detectChanges(); 26 | const compiled = fixture.nativeElement as HTMLElement; 27 | expect(compiled.querySelector('h1')?.textContent).toContain('Hello, demo'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, QueryList, ViewChildren} from '@angular/core'; 2 | import {FormBuilder, FormGroup, Validators} from '@angular/forms'; 3 | import {SignatureFieldComponent, SignatureFieldConfig} from './signature-field/signature-field.component'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | styleUrl: './app.component.scss' 9 | }) 10 | export class AppComponent implements AfterViewInit { 11 | public items: SignatureFieldConfig[] = [ 12 | { 13 | options: {backgroundColor: 'rgb(255,255,255)'}, 14 | }, 15 | { 16 | options: {canvasBackground: 'rgb(255, 255, 255) url(assets/sign-here.png) bottom left no-repeat', penColor: 'rgb(255, 0, 0)'}, 17 | quality: 0.5, 18 | imageType: 'image/jpeg', 19 | }, 20 | { 21 | options: {canvasBackground: 'rgb(0, 0, 255) url(assets/sign-here.png) bottom left no-repeat', penColor: 'rgb(255, 255, 0)'}, 22 | quality: 0.8, 23 | imageType: 'image/jpeg', 24 | }, 25 | { 26 | options: {canvasBackground: 'url(assets/sign-here.png) bottom left no-repeat, center / cover url(assets/cat-sig.png)', canvasHeight: 400} 27 | } 28 | ]; 29 | public titles: string[] = [ 30 | 'Default with white background', 31 | 'Image Background & Red Pen Color (JPEG/Quality 50%)', 32 | 'Image with Blue Background & Red Pen Color (JPEG/Quality 80%)', 33 | 'With image as background' 34 | ]; 35 | 36 | public form: FormGroup; 37 | public result: string[] = []; 38 | 39 | @ViewChildren(SignatureFieldComponent) public sigs: QueryList; 40 | @ViewChildren('sigContainer') public sigContainer: QueryList; 41 | 42 | constructor(fb: FormBuilder) { 43 | const controls = [...Array(this.items.length)].map((value, index) => { 44 | return {[`signatureField${index}`]: ['', Validators.required]}; 45 | }); 46 | this.form = fb.group(Object.assign({}, ...controls)); 47 | } 48 | 49 | public ngAfterViewInit() { 50 | this.sigs.forEach((signature, index) => { 51 | this.size(signature); 52 | }); 53 | } 54 | 55 | public updateSigPreview(idx: number, data: string) { 56 | this.result[idx] = data; 57 | } 58 | 59 | public size(sig: SignatureFieldComponent) { 60 | sig.signaturePad.set('canvasWidth', sig.nativeElement.clientWidth - 40); 61 | sig.signaturePad.set('canvasHeight', sig.nativeElement.clientHeight); 62 | } 63 | 64 | public submit() { 65 | this.result = this.sigs.map(signature => signature.signature); 66 | console.log('CAPTURED SIGNATURES:', this.result); 67 | } 68 | 69 | public clear() { 70 | this.sigs.forEach((signature) => signature.signaturePad.clear()); 71 | this.form.reset(); 72 | this.result = []; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /projects/demo/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import {BrowserModule} from '@angular/platform-browser'; 2 | import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; 3 | import {NgModule, provideZoneChangeDetection} from '@angular/core'; 4 | import {FormsModule, ReactiveFormsModule} from '@angular/forms'; 5 | import {AppComponent} from './app.component'; 6 | import {SignatureFieldComponent} from './signature-field/signature-field.component'; 7 | import {AngularSignaturePadModule} from 'angular-signature-pad'; 8 | import {NgOptimizedImage} from "@angular/common"; 9 | import {SignatureViewComponent} from "projects/demo/src/app/signature-view/signature-view.component"; 10 | 11 | @NgModule({ 12 | declarations: [ 13 | AppComponent, 14 | SignatureFieldComponent, 15 | SignatureViewComponent 16 | ], 17 | bootstrap: [AppComponent], 18 | imports: [ 19 | BrowserModule, 20 | BrowserAnimationsModule, 21 | FormsModule, 22 | ReactiveFormsModule, 23 | AngularSignaturePadModule, 24 | NgOptimizedImage 25 | ], 26 | providers: [provideZoneChangeDetection({eventCoalescing: true})] 27 | }) 28 | export class AppModule { 29 | } 30 | -------------------------------------------------------------------------------- /projects/demo/src/app/signature-field/signature-field.component.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /projects/demo/src/app/signature-field/signature-field.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | signature-pad { 3 | width: 100%; 4 | max-width: 650px; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /projects/demo/src/app/signature-field/signature-field.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, EventEmitter, forwardRef, Input, Output, ViewChild} from '@angular/core'; 2 | import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; 3 | import {SignaturePadComponent} from 'angular-signature-pad'; 4 | import {NgSignaturePadOptions} from "projects/angular-signature-pad/src/lib/angular-signature-pad.component"; 5 | 6 | export type SignatureFieldConfig = { 7 | options: NgSignaturePadOptions; 8 | quality?: number; 9 | imageType?: string; 10 | }; 11 | 12 | @Component({ 13 | selector: 'app-signature-field', 14 | templateUrl: 'signature-field.component.html', 15 | styleUrl: 'signature-field.component.scss', 16 | providers: [ 17 | { 18 | provide: NG_VALUE_ACCESSOR, 19 | useExisting: forwardRef(() => SignatureFieldComponent), 20 | multi: true, 21 | }, 22 | ], 23 | }) 24 | export class SignatureFieldComponent implements ControlValueAccessor, AfterViewInit { 25 | @ViewChild(SignaturePadComponent) public signaturePad: SignaturePadComponent; 26 | 27 | @Input() options: NgSignaturePadOptions; 28 | @Input() quality: number; 29 | @Input() imageType: string; 30 | 31 | @Output() public signatureChanged: EventEmitter = new EventEmitter(); 32 | 33 | public nativeElement: HTMLElement; 34 | 35 | private _signature: string; 36 | private onChange: (value: string) => void; 37 | private onTouched: () => void; 38 | 39 | get signature(): string { 40 | return this._signature; 41 | } 42 | 43 | set signature(value: any) { 44 | this._signature = value; 45 | console.log('Set signature to ', value); 46 | this.signatureChanged.emit(value); 47 | this.onChange(this.signature); 48 | } 49 | 50 | constructor(private _elementRef: ElementRef) { 51 | this.nativeElement = this._elementRef.nativeElement; 52 | } 53 | 54 | public ngAfterViewInit(): void { 55 | this.signaturePad.clear(); 56 | } 57 | 58 | public randomBackgroundColor() { 59 | const r = Math.round(Math.random() * 255); 60 | const g = Math.round(Math.random() * 255); 61 | const b = Math.round(Math.random() * 255); 62 | 63 | this.signaturePad.changeBackgroundColor(`rgb(${r},${g},${b})`); 64 | } 65 | 66 | public writeValue(value: string): void { 67 | this._signature = value; 68 | this._updateSignaturePadData(value); 69 | } 70 | 71 | public registerOnChange(fn: (value: string) => void): void { 72 | this.onChange = fn; 73 | } 74 | 75 | public registerOnTouched(fn: () => void): void { 76 | this.onTouched = fn; 77 | } 78 | 79 | public drawBegin(): void { 80 | console.log('Begin Drawing'); 81 | } 82 | 83 | public drawComplete(data: any): void { 84 | console.log('Complete Drawing', data); 85 | if (data) { 86 | this.signature = this.signaturePad.toDataURL(this.imageType, this.quality); 87 | } else { 88 | this.signature = ''; 89 | } 90 | } 91 | 92 | public clear(): void { 93 | this.signaturePad.clear(); 94 | this.signature = ''; 95 | } 96 | 97 | private _updateSignaturePadData(value: string) { 98 | if (this.signaturePad && value) { 99 | this.signaturePad.fromDataURL(value); 100 | console.log('Signature data :', this.signaturePad.toData()); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /projects/demo/src/app/signature-view/signature-view.component.html: -------------------------------------------------------------------------------- 1 |
2 | @if (signature) { 3 | Signature 4 | } @else { 5 | No signature data to preview 6 | } 7 |
8 | -------------------------------------------------------------------------------- /projects/demo/src/app/signature-view/signature-view.component.scss: -------------------------------------------------------------------------------- 1 | :host { 2 | display: block; 3 | 4 | .preview { 5 | background: #e7e6e6; 6 | } 7 | 8 | img { 9 | display: block; 10 | margin: 0 auto; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /projects/demo/src/app/signature-view/signature-view.component.ts: -------------------------------------------------------------------------------- 1 | import {AfterViewInit, Component, ElementRef, forwardRef, Input, ViewChild} from '@angular/core'; 2 | import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; 3 | import {SignaturePadComponent} from 'angular-signature-pad'; 4 | import {NgSignaturePadOptions} from "projects/angular-signature-pad/src/lib/angular-signature-pad.component"; 5 | 6 | 7 | @Component({ 8 | selector: 'app-signature-view', 9 | templateUrl: 'signature-view.component.html', 10 | styleUrl: 'signature-view.component.scss' 11 | }) 12 | export class SignatureViewComponent { 13 | @Input() signature: string; 14 | 15 | public nativeElement: HTMLElement; 16 | 17 | constructor(private _elementRef: ElementRef) { 18 | this.nativeElement = this._elementRef.nativeElement; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: false 3 | }; 4 | -------------------------------------------------------------------------------- /projects/demo/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SignaturepadDemo 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 | platformBrowserDynamic().bootstrapModule(AppModule) 11 | .catch(err => console.error(err)); 12 | -------------------------------------------------------------------------------- /projects/demo/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/demo", 7 | "types": [] 8 | }, 9 | "files": [ 10 | "src/main.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /projects/demo/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | /* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ 2 | /* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ 3 | { 4 | "extends": "../../tsconfig.json", 5 | "compilerOptions": { 6 | "outDir": "../../out-tsc/spec", 7 | "types": [ 8 | "jasmine" 9 | ] 10 | }, 11 | "include": [ 12 | "src/**/*.spec.ts", 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "esModuleInterop": true, 8 | "declaration": false, 9 | "experimentalDecorators": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "target": "ES2022", 13 | "module": "ES2022", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "ESNext", 19 | "DOM" 20 | ], 21 | "paths": { 22 | "angular-signature-pad": [ 23 | "dist/angular-signature-pad", 24 | "dist/" 25 | ], 26 | "demo": [ 27 | "demo" 28 | ] 29 | }, 30 | "useDefineForClassFields": false 31 | }, 32 | "angularCompilerOptions": { 33 | "fullTemplateTypeCheck": true, 34 | "strictInjectionParameters": true 35 | } 36 | } 37 | --------------------------------------------------------------------------------