├── .github ├── FUNDING.yml └── workflows │ └── on-tag-creation-deploy.yml ├── .gitignore ├── CNAME ├── README.md ├── angular-17 ├── .editorconfig ├── .gitignore ├── .vscode │ ├── extensions.json │ ├── launch.json │ └── tasks.json ├── README.md ├── angular.json ├── package-lock.json ├── package.json ├── server.ts ├── src │ ├── app │ │ ├── app.component.html │ │ ├── app.component.scss │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.config.server.ts │ │ ├── app.config.ts │ │ ├── app.routes.ts │ │ └── steps │ │ │ └── step-2 │ │ │ ├── step-2.component.html │ │ │ ├── step-2.component.scss │ │ │ └── step-2.component.ts │ ├── assets │ │ ├── .gitkeep │ │ ├── bouton.png │ │ ├── domoticz-150x150.png │ │ ├── france-national-world.svg │ │ ├── result_Off.png │ │ ├── result_On.png │ │ └── united_states-national-world.svg │ ├── favicon.ico │ ├── index.html │ ├── main.server.ts │ ├── main.ts │ └── styles.scss ├── tsconfig.app.json ├── tsconfig.json └── tsconfig.spec.json ├── css ├── angular-toastr.css ├── app.css ├── bootstrap.min.css ├── icon.css └── ng-img-crop.css ├── font ├── material-design-icons │ ├── LICENSE.txt │ ├── Material-Design-Icons.eot │ ├── Material-Design-Icons.svg │ ├── Material-Design-Icons.ttf │ ├── Material-Design-Icons.woff │ └── Material-Design-Icons.woff2 └── roboto │ ├── Roboto-Bold.eot │ ├── Roboto-Bold.ttf │ ├── Roboto-Bold.woff │ ├── Roboto-Bold.woff2 │ ├── Roboto-Light.eot │ ├── Roboto-Light.ttf │ ├── Roboto-Light.woff │ ├── Roboto-Light.woff2 │ ├── Roboto-Medium.eot │ ├── Roboto-Medium.ttf │ ├── Roboto-Medium.woff │ ├── Roboto-Medium.woff2 │ ├── Roboto-Regular.eot │ ├── Roboto-Regular.ttf │ ├── Roboto-Regular.woff │ ├── Roboto-Regular.woff2 │ ├── Roboto-Thin.eot │ ├── Roboto-Thin.ttf │ ├── Roboto-Thin.woff │ └── Roboto-Thin.woff2 ├── fonts └── roboto │ ├── Roboto-Bold.eot │ ├── Roboto-Bold.ttf │ ├── Roboto-Bold.woff │ ├── Roboto-Bold.woff2 │ ├── Roboto-Light.eot │ ├── Roboto-Light.ttf │ ├── Roboto-Light.woff │ ├── Roboto-Light.woff2 │ ├── Roboto-Medium.eot │ ├── Roboto-Medium.ttf │ ├── Roboto-Medium.woff │ ├── Roboto-Medium.woff2 │ ├── Roboto-Regular.eot │ ├── Roboto-Regular.ttf │ ├── Roboto-Regular.woff │ ├── Roboto-Regular.woff2 │ ├── Roboto-Thin.eot │ ├── Roboto-Thin.ttf │ ├── Roboto-Thin.woff │ └── Roboto-Thin.woff2 ├── img ├── bouton.png ├── domoticz-150x150.png ├── france-national-world.svg ├── result_Off.png ├── result_On.png └── united_states-national-world.svg ├── index.html ├── js ├── app.controller.js ├── ng-img-crop.js └── translate.config.js ├── lib ├── FileSaver.js ├── angular-animate.js ├── angular-toastr.tpls.js ├── angular-translate.min.js ├── angular.min.js ├── base64.js ├── canvas2image.js ├── canvas2image.min.js ├── html2canvas.js └── jszip.min.js ├── package.json └── screenshots └── screen1.png /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: AurelienLoyer 2 | custom: ["https://paypal.me/AurelienLoyer"] 3 | -------------------------------------------------------------------------------- /.github/workflows/on-tag-creation-deploy.yml: -------------------------------------------------------------------------------- 1 | name: GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - master # Set a branch name to trigger deployment 7 | paths: 8 | - 'angular-17' 9 | - '.github/workflows/on-tag-creation-deploy.yml' 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-22.04 14 | permissions: 15 | contents: write 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.ref }} 18 | steps: 19 | - uses: actions/checkout@v3 20 | - uses: actions/setup-node@v2 21 | with: 22 | node-version: 20.x 23 | - run: npm run ci:dev 24 | - name: Deploy 25 | uses: peaceiris/actions-gh-pages@v4 26 | with: 27 | github_token: ${{ secrets.GITHUB_TOKEN }} 28 | publish_dir: ./angular-17/dist/domoticz_custom_icon_generator/browser -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | .DS_Store -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | domoticz-icon.aurelien-loyer.fr -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🤖 🆒 Domoticz custom icon generator 2 | 3 | 4 |

5 | 👇👇👇👇👇👇👇👇👇 6 |
7 | 👉 Online version 👈 8 |
9 | ☝️☝️☝️☝️☝️☝️☝️☝️☝️ 10 |
11 |
12 |

13 | 14 | ## Description 📚 15 |
16 | 17 | 🤖 🆒 Domoticz custom icon generator, help people to generate custom icon, resize image, crop image 🔪, and generate zip folder 📦 to have amazing 🎉 icons in Domoticz app. 18 | If you want to contribute to this project: Github 🐙 or contact me on Twitter 🐦 19 | 20 |
21 | 22 | ## Feature / Issue / Bug / 🐛 ? 23 |
24 | 25 | 👉 Create a new issue ! or 👉 pull request ! 26 | 27 |
28 | 29 | 30 | ## Preview 👀 31 |
32 | 33 | 34 | 35 |
36 | 37 | ## Todo ✅ 38 |
39 | 40 | - [x] Integrate AngularJs 41 | - [x] Convert Div -> Canvas -> image/png 42 | - [x] Zip all files 43 | - [x] Multi Language 44 | - [ ] Update framework (Angular 17 ?) 45 | - [ ] Image custom (border,border-radius) 46 | - [ ] Image blur 47 | - [ ] Resolve Bug Retina 48 | - [ ] News features ? 49 | -------------------------------------------------------------------------------- /angular-17/.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 | -------------------------------------------------------------------------------- /angular-17/.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 | /bazel-out 8 | 9 | # Node 10 | /node_modules 11 | npm-debug.log 12 | yarn-error.log 13 | 14 | # IDEs and editors 15 | .idea/ 16 | .project 17 | .classpath 18 | .c9/ 19 | *.launch 20 | .settings/ 21 | *.sublime-workspace 22 | 23 | # Visual Studio Code 24 | .vscode/* 25 | !.vscode/settings.json 26 | !.vscode/tasks.json 27 | !.vscode/launch.json 28 | !.vscode/extensions.json 29 | .history/* 30 | 31 | # Miscellaneous 32 | /.angular/cache 33 | .sass-cache/ 34 | /connect.lock 35 | /coverage 36 | /libpeerconnection.log 37 | testem.log 38 | /typings 39 | 40 | # System files 41 | .DS_Store 42 | Thumbs.db 43 | -------------------------------------------------------------------------------- /angular-17/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846 3 | "recommendations": ["angular.ng-template"] 4 | } 5 | -------------------------------------------------------------------------------- /angular-17/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 3 | "version": "0.2.0", 4 | "configurations": [ 5 | { 6 | "name": "ng serve", 7 | "type": "chrome", 8 | "request": "launch", 9 | "preLaunchTask": "npm: start", 10 | "url": "http://localhost:4200/" 11 | }, 12 | { 13 | "name": "ng test", 14 | "type": "chrome", 15 | "request": "launch", 16 | "preLaunchTask": "npm: test", 17 | "url": "http://localhost:9876/debug.html" 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /angular-17/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558 3 | "version": "2.0.0", 4 | "tasks": [ 5 | { 6 | "type": "npm", 7 | "script": "start", 8 | "isBackground": true, 9 | "problemMatcher": { 10 | "owner": "typescript", 11 | "pattern": "$tsc", 12 | "background": { 13 | "activeOnStart": true, 14 | "beginsPattern": { 15 | "regexp": "(.*?)" 16 | }, 17 | "endsPattern": { 18 | "regexp": "bundle generation complete" 19 | } 20 | } 21 | } 22 | }, 23 | { 24 | "type": "npm", 25 | "script": "test", 26 | "isBackground": true, 27 | "problemMatcher": { 28 | "owner": "typescript", 29 | "pattern": "$tsc", 30 | "background": { 31 | "activeOnStart": true, 32 | "beginsPattern": { 33 | "regexp": "(.*?)" 34 | }, 35 | "endsPattern": { 36 | "regexp": "bundle generation complete" 37 | } 38 | } 39 | } 40 | } 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /angular-17/README.md: -------------------------------------------------------------------------------- 1 | # DomoticzCustomIconGenerator 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.3.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities. 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /angular-17/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "domoticz_custom_icon_generator": { 7 | "projectType": "application", 8 | "schematics": { 9 | "@schematics/angular:component": { 10 | "style": "scss" 11 | } 12 | }, 13 | "root": "", 14 | "sourceRoot": "src", 15 | "prefix": "app", 16 | "architect": { 17 | "build": { 18 | "builder": "@angular-devkit/build-angular:application", 19 | "options": { 20 | "outputPath": "dist/domoticz_custom_icon_generator", 21 | "index": "src/index.html", 22 | "browser": "src/main.ts", 23 | "polyfills": [ 24 | "zone.js" 25 | ], 26 | "tsConfig": "tsconfig.app.json", 27 | "inlineStyleLanguage": "scss", 28 | "assets": [ 29 | "src/favicon.ico", 30 | "src/assets" 31 | ], 32 | "styles": [ 33 | "src/styles.scss" 34 | ], 35 | "scripts": [], 36 | "server": "src/main.server.ts", 37 | "prerender": true, 38 | "ssr": false 39 | }, 40 | "configurations": { 41 | "production": { 42 | "budgets": [ 43 | { 44 | "type": "initial", 45 | "maximumWarning": "500kb", 46 | "maximumError": "1mb" 47 | }, 48 | { 49 | "type": "anyComponentStyle", 50 | "maximumWarning": "2kb", 51 | "maximumError": "4kb" 52 | } 53 | ], 54 | "outputHashing": "all" 55 | }, 56 | "development": { 57 | "optimization": false, 58 | "extractLicenses": false, 59 | "sourceMap": true 60 | } 61 | }, 62 | "defaultConfiguration": "production" 63 | }, 64 | "serve": { 65 | "builder": "@angular-devkit/build-angular:dev-server", 66 | "configurations": { 67 | "production": { 68 | "buildTarget": "domoticz_custom_icon_generator:build:production" 69 | }, 70 | "development": { 71 | "buildTarget": "domoticz_custom_icon_generator:build:development" 72 | } 73 | }, 74 | "defaultConfiguration": "development" 75 | }, 76 | "extract-i18n": { 77 | "builder": "@angular-devkit/build-angular:extract-i18n", 78 | "options": { 79 | "buildTarget": "domoticz_custom_icon_generator:build" 80 | } 81 | }, 82 | "test": { 83 | "builder": "@angular-devkit/build-angular:karma", 84 | "options": { 85 | "polyfills": [ 86 | "zone.js", 87 | "zone.js/testing" 88 | ], 89 | "tsConfig": "tsconfig.spec.json", 90 | "inlineStyleLanguage": "scss", 91 | "assets": [ 92 | "src/favicon.ico", 93 | "src/assets" 94 | ], 95 | "styles": [ 96 | "src/styles.scss" 97 | ], 98 | "scripts": [] 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /angular-17/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "domoticz-custom-icon-generator", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "watch": "ng build --watch --configuration development", 9 | "test": "ng test", 10 | "serve:ssr:domoticz_custom_icon_generator": "node dist/domoticz_custom_icon_generator/server/server.mjs" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "^17.3.0", 15 | "@angular/common": "^17.3.0", 16 | "@angular/compiler": "^17.3.0", 17 | "@angular/core": "^17.3.0", 18 | "@angular/forms": "^17.3.0", 19 | "@angular/platform-browser": "^17.3.0", 20 | "@angular/platform-browser-dynamic": "^17.3.0", 21 | "@angular/platform-server": "^17.3.0", 22 | "@angular/router": "^17.3.0", 23 | "@angular/ssr": "^17.3.8", 24 | "express": "^4.18.2", 25 | "file-saver": "^2.0.5", 26 | "jszip": "^3.10.1", 27 | "ngx-image-cropper": "^8.0.0", 28 | "rxjs": "~7.8.0", 29 | "tslib": "^2.3.0", 30 | "zone.js": "~0.14.3" 31 | }, 32 | "devDependencies": { 33 | "@angular-devkit/build-angular": "^17.3.0", 34 | "@angular/cli": "^17.3.0", 35 | "@angular/compiler-cli": "^17.3.0", 36 | "@types/express": "^4.17.17", 37 | "@types/file-saver": "^2.0.7", 38 | "@types/jasmine": "~5.1.0", 39 | "@types/node": "^18.18.0", 40 | "jasmine-core": "~5.1.0", 41 | "karma": "~6.4.0", 42 | "karma-chrome-launcher": "~3.2.0", 43 | "karma-coverage": "~2.2.0", 44 | "karma-jasmine": "~5.1.0", 45 | "karma-jasmine-html-reporter": "~2.1.0", 46 | "typescript": "~5.4.2" 47 | } 48 | } -------------------------------------------------------------------------------- /angular-17/server.ts: -------------------------------------------------------------------------------- 1 | import { APP_BASE_HREF } from '@angular/common'; 2 | import { CommonEngine } from '@angular/ssr'; 3 | import express from 'express'; 4 | import { fileURLToPath } from 'node:url'; 5 | import { dirname, join, resolve } from 'node:path'; 6 | import bootstrap from './src/main.server'; 7 | 8 | // The Express app is exported so that it can be used by serverless Functions. 9 | export function app(): express.Express { 10 | const server = express(); 11 | const serverDistFolder = dirname(fileURLToPath(import.meta.url)); 12 | const browserDistFolder = resolve(serverDistFolder, '../browser'); 13 | const indexHtml = join(serverDistFolder, 'index.server.html'); 14 | 15 | const commonEngine = new CommonEngine(); 16 | 17 | server.set('view engine', 'html'); 18 | server.set('views', browserDistFolder); 19 | 20 | // Example Express Rest API endpoints 21 | // server.get('/api/**', (req, res) => { }); 22 | // Serve static files from /browser 23 | server.get('*.*', express.static(browserDistFolder, { 24 | maxAge: '1y' 25 | })); 26 | 27 | // All regular routes use the Angular engine 28 | server.get('*', (req, res, next) => { 29 | const { protocol, originalUrl, baseUrl, headers } = req; 30 | 31 | commonEngine 32 | .render({ 33 | bootstrap, 34 | documentFilePath: indexHtml, 35 | url: `${protocol}://${headers.host}${originalUrl}`, 36 | publicPath: browserDistFolder, 37 | providers: [{ provide: APP_BASE_HREF, useValue: baseUrl }], 38 | }) 39 | .then((html) => res.send(html)) 40 | .catch((err) => next(err)); 41 | }); 42 | 43 | return server; 44 | } 45 | 46 | function run(): void { 47 | const port = process.env['PORT'] || 4000; 48 | 49 | // Start up the Node server 50 | const server = app(); 51 | server.listen(port, () => { 52 | console.log(`Node Express server listening on http://localhost:${port}`); 53 | }); 54 | } 55 | 56 | run(); 57 | -------------------------------------------------------------------------------- /angular-17/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 10 | 11 |

Domoticz Custom icon generator

12 |
13 | fr flag 19 | 22 |
23 |
24 | 25 |
26 |
27 |
28 | 🤖 🆒 Domoticz custom icon generator, help people to generate custom 29 | icon, resize image, crop image 🔪, and generate zip folder 📦 to have 30 | amazing 🎉 icons in Domoticz app. If you want to contribute to this 31 | project: 32 | 36 | Github 🐙 37 | 38 | or contact me on 39 | 40 | Twitter 🐦 41 | 42 | 43 |

44 | Enjoy 🤩 45 |
46 |
47 |
48 | 49 |
50 |
51 |
52 | 53 |

1. Icon name

54 | 55 |
56 |
57 | 58 | 63 | 64 |
65 |
66 |

67 | 3. Image settings 68 |

69 |

70 | Work in progress (Border radius,Border...) 71 |

72 |
73 |
74 |

Bord arrondi

75 | 82 |
85 | 93 |
94 |
95 |

Contour de l'image

96 | 103 |
114 | 123 | 124 |
125 |
126 |
127 | 128 |
129 |

3. To finish

130 | 138 |
139 |
140 |
141 | 142 | 180 | 181 | 190 |
191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /angular-17/src/app/app.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/app/app.component.scss -------------------------------------------------------------------------------- /angular-17/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 'domoticz_custom_icon_generator' title`, () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app.title).toEqual('domoticz_custom_icon_generator'); 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, domoticz_custom_icon_generator'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /angular-17/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, AfterViewInit } from '@angular/core'; 2 | import { RouterOutlet } from '@angular/router'; 3 | import { NgIf } from '@angular/common'; 4 | import { FormsModule } from '@angular/forms'; 5 | import JSZip from 'jszip'; 6 | import { saveAs } from 'file-saver'; 7 | import { Step2Component } from './steps/step-2/step-2.component'; 8 | 9 | type OnOff = 'on' | 'off'; 10 | 11 | @Component({ 12 | selector: 'app-root', 13 | standalone: true, 14 | imports: [RouterOutlet, NgIf, FormsModule, Step2Component], 15 | templateUrl: './app.component.html', 16 | styleUrl: './app.component.scss' 17 | }) 18 | export class AppComponent implements AfterViewInit { 19 | 20 | public croppedImageOnBlob: Blob | undefined | null = undefined; 21 | public croppedImageOffBlob: Blob | undefined | null = undefined; 22 | public isAppLoaded = false; 23 | public iconName = ''; 24 | 25 | public ngAfterViewInit() { 26 | this.isAppLoaded = true; 27 | } 28 | 29 | public assignCropperImageBlob(blob: Blob, onOff: OnOff) { 30 | if (onOff === 'on') { 31 | this.croppedImageOnBlob = blob; 32 | } 33 | else { 34 | this.croppedImageOffBlob = blob; 35 | } 36 | } 37 | 38 | public zip() { 39 | 40 | if (!this.croppedImageOnBlob) { 41 | return; 42 | } 43 | 44 | if (!this.croppedImageOffBlob) { 45 | return; 46 | } 47 | 48 | var zip = new JSZip(); 49 | 50 | zip.file("icons.txt", this.iconName + ";Button " + this.iconName + ";Icon " + this.iconName + " generate via " + window.location.href); 51 | 52 | // var preview = toDataUrl('../img/bouton.png'); 53 | 54 | zip.file(this.iconName + "48_On.png", this.croppedImageOnBlob, { base64: true }); 55 | zip.file(this.iconName + "48_Off.png", this.croppedImageOffBlob, { base64: true }); 56 | // zip.file(this.iconName + '.png', preview, { base64: true }); 57 | 58 | // vm.loader = true; 59 | zip.generateAsync({ type: "blob" }).then((content) => { 60 | // vm.loader = false; 61 | saveAs(content, "domoticz_custom_icon_" + this.iconName + ".zip"); 62 | }); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /angular-17/src/app/app.config.server.ts: -------------------------------------------------------------------------------- 1 | import { mergeApplicationConfig, ApplicationConfig } from '@angular/core'; 2 | import { provideServerRendering } from '@angular/platform-server'; 3 | import { appConfig } from './app.config'; 4 | 5 | const serverConfig: ApplicationConfig = { 6 | providers: [ 7 | provideServerRendering() 8 | ] 9 | }; 10 | 11 | export const config = mergeApplicationConfig(appConfig, serverConfig); 12 | -------------------------------------------------------------------------------- /angular-17/src/app/app.config.ts: -------------------------------------------------------------------------------- 1 | import { ApplicationConfig } from '@angular/core'; 2 | import { provideRouter } from '@angular/router'; 3 | 4 | import { routes } from './app.routes'; 5 | import { provideClientHydration } from '@angular/platform-browser'; 6 | 7 | export const appConfig: ApplicationConfig = { 8 | providers: [provideRouter(routes), provideClientHydration()] 9 | }; 10 | -------------------------------------------------------------------------------- /angular-17/src/app/app.routes.ts: -------------------------------------------------------------------------------- 1 | import { Routes } from '@angular/router'; 2 | 3 | export const routes: Routes = []; 4 | -------------------------------------------------------------------------------- /angular-17/src/app/steps/step-2/step-2.component.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |

2.1 ON Button

5 | 6 |
7 | 8 | Select an image file: 9 | 14 |
15 | 16 |
17 | 21 | 33 |
34 |
35 |
36 | 37 |

2.2 OFF Button

38 | 39 |
40 | 41 | Select an image file: 42 | 47 |
48 | 49 |
50 | 54 | 66 |
67 |
68 |
69 |
70 |
71 | 72 | 73 |
74 | 75 |
76 | 79 |
80 | 81 | 82 |
83 |
84 |
85 |
86 |
87 | 88 | 89 |
90 | 91 |
92 | 95 |
96 | 97 | 98 |
99 |
100 |
101 |
102 |
103 | -------------------------------------------------------------------------------- /angular-17/src/app/steps/step-2/step-2.component.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/app/steps/step-2/step-2.component.scss -------------------------------------------------------------------------------- /angular-17/src/app/steps/step-2/step-2.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, EventEmitter, Output } from '@angular/core'; 2 | import { NgIf } from '@angular/common'; 3 | import { ImageCropperComponent, ImageCroppedEvent, LoadedImage } from 'ngx-image-cropper'; 4 | import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; 5 | 6 | type OnOff = 'on' | 'off'; 7 | 8 | @Component({ 9 | selector: 'app-step-2', 10 | standalone: true, 11 | imports: [NgIf, ImageCropperComponent], 12 | templateUrl: './step-2.component.html', 13 | styleUrl: './step-2.component.scss' 14 | }) 15 | export class Step2Component { 16 | 17 | public imageOnChangedEvent: Event | null = null; 18 | public imageOffChangedEvent: Event | null = null; 19 | public croppedImageOn: SafeUrl = ''; 20 | public croppedImageOff: SafeUrl = ''; 21 | 22 | @Output() public assignCropperImageOnBlob: EventEmitter = new EventEmitter(); 23 | @Output() public assignCropperImageOffBlob: EventEmitter = new EventEmitter(); 24 | 25 | constructor( 26 | private sanitizer: DomSanitizer 27 | ) { 28 | } 29 | 30 | public fileChangeEvent(event: Event, onOff: OnOff): void { 31 | if (onOff === 'on') { 32 | this.imageOnChangedEvent = event; 33 | } 34 | else { 35 | this.imageOffChangedEvent = event; 36 | } 37 | } 38 | 39 | public imageCropped(event: ImageCroppedEvent, onOff: OnOff) { 40 | if (!event.objectUrl) { 41 | console.log(event); 42 | 43 | return; 44 | } 45 | 46 | if (onOff === 'on' && event.blob) { 47 | this.croppedImageOn = this.sanitizer.bypassSecurityTrustUrl(event.objectUrl); 48 | this.assignCropperImageOnBlob.emit(event.blob); 49 | } 50 | else if (onOff === 'off' && event.blob) { 51 | this.croppedImageOff = this.sanitizer.bypassSecurityTrustUrl(event.objectUrl); 52 | this.assignCropperImageOffBlob.emit(event.blob); 53 | } else { 54 | console.log(event); 55 | } 56 | // event.blob can be used to upload the cropped image 57 | } 58 | 59 | imageLoaded(image: LoadedImage) { 60 | // show cropper 61 | } 62 | 63 | cropperReady() { 64 | // cropper ready 65 | } 66 | 67 | loadImageFailed() { 68 | // show message 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /angular-17/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/assets/.gitkeep -------------------------------------------------------------------------------- /angular-17/src/assets/bouton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/assets/bouton.png -------------------------------------------------------------------------------- /angular-17/src/assets/domoticz-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/assets/domoticz-150x150.png -------------------------------------------------------------------------------- /angular-17/src/assets/france-national-world.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular-17/src/assets/result_Off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/assets/result_Off.png -------------------------------------------------------------------------------- /angular-17/src/assets/result_On.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/assets/result_On.png -------------------------------------------------------------------------------- /angular-17/src/assets/united_states-national-world.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /angular-17/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/angular-17/src/favicon.ico -------------------------------------------------------------------------------- /angular-17/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 🤖 🆒 Domoticz custom icon generator 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /angular-17/src/main.server.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { AppComponent } from './app/app.component'; 3 | import { config } from './app/app.config.server'; 4 | 5 | const bootstrap = () => bootstrapApplication(AppComponent, config); 6 | 7 | export default bootstrap; 8 | -------------------------------------------------------------------------------- /angular-17/src/main.ts: -------------------------------------------------------------------------------- 1 | import { bootstrapApplication } from '@angular/platform-browser'; 2 | import { appConfig } from './app/app.config'; 3 | import { AppComponent } from './app/app.component'; 4 | 5 | bootstrapApplication(AppComponent, appConfig) 6 | .catch((err) => console.error(err)); 7 | -------------------------------------------------------------------------------- /angular-17/src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | 3 | :root { 4 | --main-bg-color: #673ab7; 5 | --second-bg-color: rgb(75, 34, 148); 6 | } 7 | 8 | body, 9 | html { 10 | margin: 0; 11 | padding: 0; 12 | text-align: center; 13 | font-family: 'Varela Round', sans-serif; 14 | } 15 | 16 | .description { 17 | padding: 50px 18%; 18 | padding-top: 100px; 19 | font-size: 1.2em; 20 | } 21 | 22 | header { 23 | background-color: var(--main-bg-color); 24 | height: 50px; 25 | display: flex; 26 | color: white; 27 | align-items: center; 28 | justify-content: space-between; 29 | padding: 0px 2%; 30 | position: fixed; 31 | width: 100%; 32 | z-index: 9999; 33 | } 34 | 35 | header a { 36 | height: 100%; 37 | } 38 | 39 | header .logo { 40 | margin-top: 10%; 41 | height: 80%; 42 | border: 2px solid white; 43 | } 44 | 45 | header h1 { 46 | font-size: 1.5em; 47 | } 48 | 49 | .settings .flag { 50 | transition: all 0.2s; 51 | height: 50px; 52 | } 53 | 54 | .settings .flag:hover { 55 | transform: scale(1.1); 56 | } 57 | 58 | h2 { 59 | margin: 30px; 60 | text-align: left; 61 | } 62 | 63 | h2 em { 64 | text-align: center; 65 | background-color: var(--main-bg-color); 66 | color: white; 67 | width: 55px; 68 | display: inline-block; 69 | font-size: 20px; 70 | line-height: 40px; 71 | } 72 | 73 | input[type='text'] { 74 | border: 3px solid var(--main-bg-color); 75 | } 76 | 77 | .cropArea { 78 | margin: 20px auto; 79 | background: #e4e4e4; 80 | overflow: hidden; 81 | width: 500px; 82 | height: 350px; 83 | } 84 | 85 | .result_On, 86 | .result_Off { 87 | width: 390px; 88 | height: 130px; 89 | margin: 20px auto; 90 | position: relative; 91 | } 92 | 93 | .result_On { 94 | background-image: url('assets/result_On.png'); 95 | } 96 | .result_Off { 97 | background-image: url('assets/result_Off.png'); 98 | } 99 | 100 | .result_On .title, 101 | .result_Off .title { 102 | background-color: #d4e1ed; 103 | width: 300px; 104 | height: 20px; 105 | top: 12px; 106 | display: block; 107 | text-align: left; 108 | position: absolute; 109 | left: 20px; 110 | font-weight: bold; 111 | } 112 | 113 | .result_On .image, 114 | .result_Off .image { 115 | width: 50px; 116 | height: 50px; 117 | background-color: #f2f1fa; 118 | background-size: cover; 119 | position: absolute; 120 | top: 37px; 121 | left: 12px; 122 | text-align: center; 123 | overflow: hidden; 124 | } 125 | .result_On .image > div, 126 | .result_Off .image > div { 127 | overflow: hidden; 128 | width: 48px; 129 | height: 48px; 130 | } 131 | 132 | .result_On .image img, 133 | .result_Off .image img { 134 | width: 48px; 135 | height: 48px; 136 | } 137 | 138 | .btn-success { 139 | background: var(--main-bg-color); 140 | } 141 | 142 | .btn-success:hover { 143 | background: var(--second-bg-color); 144 | } 145 | 146 | .preview_bloc h2 { 147 | text-align: center; 148 | } 149 | 150 | footer { 151 | color: white; 152 | margin-top: 100px; 153 | padding: 35px; 154 | width: 100%; 155 | background: var(--main-bg-color); 156 | } 157 | 158 | footer .icons { 159 | margin-bottom: 30px; 160 | font-size: 2.5em; 161 | } 162 | 163 | footer .icons a { 164 | text-decoration: none; 165 | padding: 0 20px; 166 | color: white; 167 | } 168 | 169 | footer .icons i { 170 | transition: all 0.2s; 171 | } 172 | 173 | footer .icons i:hover { 174 | transform: scale(1.2); 175 | } 176 | 177 | .footer-fix { 178 | width: 100%; 179 | position: fixed; 180 | z-index: 999; 181 | bottom: 0px; 182 | color: white; 183 | background: var(--main-bg-color); 184 | padding: 10px; 185 | } 186 | 187 | .copyright .fa-heart { 188 | color: red; 189 | } 190 | 191 | .copyright a { 192 | color: black; 193 | } 194 | 195 | .copyright i { 196 | padding: 0px 5px; 197 | } 198 | -------------------------------------------------------------------------------- /angular-17/tsconfig.app.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/app", 6 | "types": [ 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/main.ts", 12 | "src/main.server.ts", 13 | "server.ts" 14 | ], 15 | "include": [ 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /angular-17/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "outDir": "./dist/out-tsc", 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "noImplicitOverride": true, 9 | "noPropertyAccessFromIndexSignature": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "skipLibCheck": true, 13 | "esModuleInterop": true, 14 | "sourceMap": true, 15 | "declaration": false, 16 | "experimentalDecorators": true, 17 | "moduleResolution": "node", 18 | "importHelpers": true, 19 | "target": "ES2022", 20 | "module": "ES2022", 21 | "useDefineForClassFields": false, 22 | "lib": [ 23 | "ES2022", 24 | "dom" 25 | ] 26 | }, 27 | "angularCompilerOptions": { 28 | "enableI18nLegacyMessageIdFormat": false, 29 | "strictInjectionParameters": true, 30 | "strictInputAccessModifiers": true, 31 | "strictTemplates": true 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /angular-17/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 | "include": [ 11 | "src/**/*.spec.ts", 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /css/angular-toastr.css: -------------------------------------------------------------------------------- 1 | .toast-title { 2 | font-weight: bold; 3 | } 4 | .toast-message { 5 | word-wrap: break-word; 6 | } 7 | .toast-message a, 8 | .toast-message label { 9 | color: #FFFFFF; 10 | } 11 | .toast-message a:hover { 12 | color: #CCCCCC; 13 | text-decoration: none; 14 | } 15 | .toast-close-button { 16 | position: relative; 17 | right: -0.3em; 18 | top: -0.3em; 19 | float: right; 20 | font-size: 20px; 21 | font-weight: bold; 22 | color: #FFFFFF; 23 | -webkit-text-shadow: 0 1px 0 #ffffff; 24 | text-shadow: 0 1px 0 #ffffff; 25 | opacity: 0.8; 26 | } 27 | .toast-close-button:hover, 28 | .toast-close-button:focus { 29 | color: #000000; 30 | text-decoration: none; 31 | cursor: pointer; 32 | opacity: 0.4; 33 | } 34 | /*Additional properties for button version 35 | iOS requires the button element instead of an anchor tag. 36 | If you want the anchor version, it requires `href="#"`.*/ 37 | button.toast-close-button { 38 | padding: 0; 39 | cursor: pointer; 40 | background: transparent; 41 | border: 0; 42 | -webkit-appearance: none; 43 | } 44 | .toast-top-center { 45 | top: 0; 46 | right: 0; 47 | width: 100%; 48 | } 49 | .toast-bottom-center { 50 | bottom: 0; 51 | right: 0; 52 | width: 100%; 53 | } 54 | .toast-top-full-width { 55 | top: 0; 56 | right: 0; 57 | width: 100%; 58 | } 59 | .toast-bottom-full-width { 60 | bottom: 0; 61 | right: 0; 62 | width: 100%; 63 | } 64 | .toast-top-left { 65 | top: 12px; 66 | left: 12px; 67 | } 68 | .toast-top-right { 69 | top: 12px; 70 | right: 12px; 71 | } 72 | .toast-bottom-right { 73 | right: 12px; 74 | bottom: 12px; 75 | } 76 | .toast-bottom-left { 77 | bottom: 12px; 78 | left: 12px; 79 | } 80 | #toast-container { 81 | position: fixed; 82 | z-index: 999999; 83 | /*overrides*/ 84 | } 85 | #toast-container * { 86 | -moz-box-sizing: border-box; 87 | -webkit-box-sizing: border-box; 88 | box-sizing: border-box; 89 | } 90 | #toast-container > div { 91 | position: relative; 92 | overflow: hidden; 93 | margin: 0 0 6px; 94 | padding: 15px 15px 15px 50px; 95 | width: 300px; 96 | -moz-border-radius: 3px 3px 3px 3px; 97 | -webkit-border-radius: 3px 3px 3px 3px; 98 | border-radius: 3px 3px 3px 3px; 99 | background-position: 15px center; 100 | background-repeat: no-repeat; 101 | -moz-box-shadow: 0 0 12px #999999; 102 | -webkit-box-shadow: 0 0 12px #999999; 103 | box-shadow: 0 0 12px #999999; 104 | color: #FFFFFF; 105 | opacity: 0.8; 106 | } 107 | #toast-container > :hover { 108 | -moz-box-shadow: 0 0 12px #000000; 109 | -webkit-box-shadow: 0 0 12px #000000; 110 | box-shadow: 0 0 12px #000000; 111 | opacity: 1; 112 | cursor: pointer; 113 | } 114 | #toast-container > .toast-info { 115 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important; 116 | } 117 | #toast-container > .toast-error { 118 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important; 119 | } 120 | #toast-container > .toast-success { 121 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important; 122 | } 123 | #toast-container > .toast-warning { 124 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important; 125 | } 126 | #toast-container.toast-top-center > div, 127 | #toast-container.toast-bottom-center > div { 128 | width: 300px; 129 | margin: auto; 130 | } 131 | #toast-container.toast-top-full-width > div, 132 | #toast-container.toast-bottom-full-width > div { 133 | width: 96%; 134 | margin: auto; 135 | } 136 | .toast { 137 | background-color: #030303; 138 | } 139 | .toast-success { 140 | background-color: #51A351; 141 | } 142 | .toast-error { 143 | background-color: #BD362F; 144 | } 145 | .toast-info { 146 | background-color: #2F96B4; 147 | } 148 | .toast-warning { 149 | background-color: #F89406; 150 | } 151 | .toast-progress { 152 | position: absolute; 153 | left: 0; 154 | bottom: 0; 155 | height: 4px; 156 | background-color: #000000; 157 | opacity: 0.4; 158 | } 159 | /*Animations*/ 160 | .toast { 161 | opacity: 1 !important; 162 | } 163 | .toast.ng-enter { 164 | opacity: 0 !important; 165 | transition: opacity .3s linear; 166 | } 167 | .toast.ng-enter.ng-enter-active { 168 | opacity: 1 !important; 169 | } 170 | .toast.ng-leave { 171 | opacity: 1; 172 | transition: opacity .3s linear; 173 | } 174 | .toast.ng-leave.ng-leave-active { 175 | opacity: 0 !important; 176 | } 177 | /*Responsive Design*/ 178 | @media all and (max-width: 240px) { 179 | #toast-container > div { 180 | padding: 8px 8px 8px 50px; 181 | width: 11em; 182 | } 183 | #toast-container .toast-close-button { 184 | right: -0.2em; 185 | top: -0.2em; 186 | } 187 | } 188 | @media all and (min-width: 241px) and (max-width: 480px) { 189 | #toast-container > div { 190 | padding: 8px 8px 8px 50px; 191 | width: 18em; 192 | } 193 | #toast-container .toast-close-button { 194 | right: -0.2em; 195 | top: -0.2em; 196 | } 197 | } 198 | @media all and (min-width: 481px) and (max-width: 768px) { 199 | #toast-container > div { 200 | padding: 15px 15px 15px 50px; 201 | width: 25em; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /css/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --main-bg-color: #673AB7; 3 | --second-bg-color: rgb(75, 34, 148); 4 | } 5 | 6 | body,html{ 7 | margin: 0; 8 | padding: 0; 9 | text-align: center; 10 | font-family: 'Varela Round', sans-serif; 11 | } 12 | 13 | .description { 14 | padding: 50px 18%; 15 | padding-top: 100px; 16 | font-size: 1.2em; 17 | } 18 | 19 | header { 20 | background-color: var(--main-bg-color); 21 | height: 50px; 22 | display: flex; 23 | color: white; 24 | align-items: center; 25 | justify-content: space-between; 26 | padding: 0px 2%; 27 | position: fixed; 28 | width: 100%; 29 | z-index: 9999; 30 | } 31 | 32 | header .logo { 33 | height: 80%; 34 | border: 2px solid white; 35 | } 36 | 37 | header h1 { 38 | font-size: 1.5em; 39 | } 40 | 41 | .settings .flag { 42 | transition: all 0.2s; 43 | height: 50px; 44 | } 45 | 46 | .settings .flag:hover{ 47 | transform: scale(1.1); 48 | } 49 | 50 | h2{ 51 | margin: 30px; 52 | text-align: left; 53 | } 54 | 55 | h2 em{ 56 | text-align: center; 57 | background-color: var(--main-bg-color); 58 | color: white; 59 | width: 55px; 60 | display: inline-block; 61 | font-size: 20px; 62 | line-height: 40px; 63 | } 64 | 65 | input[type="text"] { 66 | border: 3px solid var(--main-bg-color); 67 | } 68 | 69 | .cropArea { 70 | margin: 20px auto; 71 | background: #E4E4E4; 72 | overflow: hidden; 73 | width:500px; 74 | height:350px; 75 | } 76 | 77 | .result_On,.result_Off{ 78 | width: 390px; 79 | height: 130px; 80 | margin: 20px auto; 81 | position: relative; 82 | } 83 | 84 | .result_On{ 85 | background-image: url('../img/result_On.png'); 86 | } 87 | .result_Off{ 88 | background-image: url('../img/result_Off.png'); 89 | } 90 | 91 | .result_On .title,.result_Off .title{ 92 | background-color: #D4E1ED; 93 | width: 300px; 94 | height: 20px; 95 | top: 12px; 96 | display: block; 97 | text-align: left; 98 | position: absolute; 99 | left: 20px; 100 | font-weight: bold; 101 | } 102 | 103 | .result_On .image,.result_Off .image{ 104 | width: 50px; 105 | height: 50px; 106 | background-color: #F2F1FA; 107 | background-size: cover; 108 | position: absolute; 109 | top: 37px; 110 | left: 12px; 111 | text-align: center; 112 | overflow: hidden; 113 | } 114 | .result_On .image > div,.result_Off .image > div{ 115 | overflow: hidden; 116 | width: 48px; 117 | height: 48px; 118 | } 119 | 120 | .result_On .image img,.result_Off .image img{ 121 | width: 48px; 122 | height: 48px; 123 | } 124 | 125 | .btn-success { 126 | background: var(--main-bg-color); 127 | } 128 | 129 | .btn-success:hover { 130 | background: var(--second-bg-color); 131 | } 132 | 133 | .preview_bloc h2{ 134 | text-align: center; 135 | } 136 | 137 | footer{ 138 | color: white; 139 | margin-top: 100px; 140 | padding:35px; 141 | width: 100%; 142 | background: var(--main-bg-color); 143 | } 144 | 145 | footer .icons{ 146 | margin-bottom: 30px; 147 | font-size: 2.5em; 148 | } 149 | 150 | footer .icons a{ 151 | text-decoration: none; 152 | padding: 0 20px; 153 | color: white; 154 | } 155 | 156 | footer .icons i{ 157 | transition: all 0.2s; 158 | } 159 | 160 | footer .icons i:hover{ 161 | transform: scale(1.2); 162 | } 163 | 164 | .footer-fix{ 165 | width: 100%; 166 | position: fixed; 167 | z-index: 999; 168 | bottom: 0px; 169 | color: white; 170 | background: var(--main-bg-color); 171 | padding: 10px; 172 | } 173 | 174 | .copyright .fa-heart { 175 | color: red; 176 | } 177 | 178 | .copyright a { 179 | color: black; 180 | } 181 | 182 | .copyright i { 183 | padding: 0px 5px; 184 | } 185 | -------------------------------------------------------------------------------- /css/icon.css: -------------------------------------------------------------------------------- 1 | /* fallback */ 2 | @font-face { 3 | font-family: 'Material Icons'; 4 | font-style: normal; 5 | font-weight: 400; 6 | src: local('Material Icons'), local('MaterialIcons-Regular'), url(http://fonts.gstatic.com/s/materialicons/v17/2fcrYFNaTjcS6g4U3t-Y5UEw0lE80llgEseQY3FEmqw.woff2) format('woff2'); 7 | } 8 | 9 | .material-icons { 10 | font-family: 'Material Icons'; 11 | font-weight: normal; 12 | font-style: normal; 13 | font-size: 24px; 14 | line-height: 1; 15 | letter-spacing: normal; 16 | text-transform: none; 17 | display: inline-block; 18 | white-space: nowrap; 19 | word-wrap: normal; 20 | direction: ltr; 21 | -webkit-font-feature-settings: 'liga'; 22 | -webkit-font-smoothing: antialiased; 23 | } 24 | -------------------------------------------------------------------------------- /css/ng-img-crop.css: -------------------------------------------------------------------------------- 1 | img-crop { 2 | width:100%; 3 | height:100%; 4 | display:block; 5 | position:relative; 6 | overflow:hidden; 7 | } 8 | img-crop canvas { 9 | display:block; 10 | position:absolute; 11 | top:50%; 12 | left:50%; 13 | outline: none; 14 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 15 | } 16 | -------------------------------------------------------------------------------- /font/material-design-icons/LICENSE.txt: -------------------------------------------------------------------------------- 1 | https://github.com/google/material-design-icons/blob/master/LICENSE 2 | https://github.com/FezVrasta/bootstrap-material-design/blob/master/fonts/LICENSE.txt 3 | 4 | Attribution-ShareAlike 4.0 International 5 | 6 | ======================================================================= 7 | 8 | Creative Commons Corporation ("Creative Commons") is not a law firm and 9 | does not provide legal services or legal advice. Distribution of 10 | Creative Commons public licenses does not create a lawyer-client or 11 | other relationship. Creative Commons makes its licenses and related 12 | information available on an "as-is" basis. Creative Commons gives no 13 | warranties regarding its licenses, any material licensed under their 14 | terms and conditions, or any related information. Creative Commons 15 | disclaims all liability for damages resulting from their use to the 16 | fullest extent possible. 17 | 18 | Using Creative Commons Public Licenses 19 | 20 | Creative Commons public licenses provide a standard set of terms and 21 | conditions that creators and other rights holders may use to share 22 | original works of authorship and other material subject to copyright 23 | and certain other rights specified in the public license below. The 24 | following considerations are for informational purposes only, are not 25 | exhaustive, and do not form part of our licenses. 26 | 27 | Considerations for licensors: Our public licenses are 28 | intended for use by those authorized to give the public 29 | permission to use material in ways otherwise restricted by 30 | copyright and certain other rights. Our licenses are 31 | irrevocable. Licensors should read and understand the terms 32 | and conditions of the license they choose before applying it. 33 | Licensors should also secure all rights necessary before 34 | applying our licenses so that the public can reuse the 35 | material as expected. Licensors should clearly mark any 36 | material not subject to the license. This includes other CC- 37 | licensed material, or material used under an exception or 38 | limitation to copyright. More considerations for licensors: 39 | wiki.creativecommons.org/Considerations_for_licensors 40 | 41 | Considerations for the public: By using one of our public 42 | licenses, a licensor grants the public permission to use the 43 | licensed material under specified terms and conditions. If 44 | the licensor's permission is not necessary for any reason--for 45 | example, because of any applicable exception or limitation to 46 | copyright--then that use is not regulated by the license. Our 47 | licenses grant only permissions under copyright and certain 48 | other rights that a licensor has authority to grant. Use of 49 | the licensed material may still be restricted for other 50 | reasons, including because others have copyright or other 51 | rights in the material. A licensor may make special requests, 52 | such as asking that all changes be marked or described. 53 | Although not required by our licenses, you are encouraged to 54 | respect those requests where reasonable. More_considerations 55 | for the public: 56 | wiki.creativecommons.org/Considerations_for_licensees 57 | 58 | ======================================================================= 59 | 60 | Creative Commons Attribution-ShareAlike 4.0 International Public 61 | License 62 | 63 | By exercising the Licensed Rights (defined below), You accept and agree 64 | to be bound by the terms and conditions of this Creative Commons 65 | Attribution-ShareAlike 4.0 International Public License ("Public 66 | License"). To the extent this Public License may be interpreted as a 67 | contract, You are granted the Licensed Rights in consideration of Your 68 | acceptance of these terms and conditions, and the Licensor grants You 69 | such rights in consideration of benefits the Licensor receives from 70 | making the Licensed Material available under these terms and 71 | conditions. 72 | 73 | 74 | Section 1 -- Definitions. 75 | 76 | a. Adapted Material means material subject to Copyright and Similar 77 | Rights that is derived from or based upon the Licensed Material 78 | and in which the Licensed Material is translated, altered, 79 | arranged, transformed, or otherwise modified in a manner requiring 80 | permission under the Copyright and Similar Rights held by the 81 | Licensor. For purposes of this Public License, where the Licensed 82 | Material is a musical work, performance, or sound recording, 83 | Adapted Material is always produced where the Licensed Material is 84 | synched in timed relation with a moving image. 85 | 86 | b. Adapter's License means the license You apply to Your Copyright 87 | and Similar Rights in Your contributions to Adapted Material in 88 | accordance with the terms and conditions of this Public License. 89 | 90 | c. BY-SA Compatible License means a license listed at 91 | creativecommons.org/compatiblelicenses, approved by Creative 92 | Commons as essentially the equivalent of this Public License. 93 | 94 | d. Copyright and Similar Rights means copyright and/or similar rights 95 | closely related to copyright including, without limitation, 96 | performance, broadcast, sound recording, and Sui Generis Database 97 | Rights, without regard to how the rights are labeled or 98 | categorized. For purposes of this Public License, the rights 99 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 100 | Rights. 101 | 102 | e. Effective Technological Measures means those measures that, in the 103 | absence of proper authority, may not be circumvented under laws 104 | fulfilling obligations under Article 11 of the WIPO Copyright 105 | Treaty adopted on December 20, 1996, and/or similar international 106 | agreements. 107 | 108 | f. Exceptions and Limitations means fair use, fair dealing, and/or 109 | any other exception or limitation to Copyright and Similar Rights 110 | that applies to Your use of the Licensed Material. 111 | 112 | g. License Elements means the license attributes listed in the name 113 | of a Creative Commons Public License. The License Elements of this 114 | Public License are Attribution and ShareAlike. 115 | 116 | h. Licensed Material means the artistic or literary work, database, 117 | or other material to which the Licensor applied this Public 118 | License. 119 | 120 | i. Licensed Rights means the rights granted to You subject to the 121 | terms and conditions of this Public License, which are limited to 122 | all Copyright and Similar Rights that apply to Your use of the 123 | Licensed Material and that the Licensor has authority to license. 124 | 125 | j. Licensor means the individual(s) or entity(ies) granting rights 126 | under this Public License. 127 | 128 | k. Share means to provide material to the public by any means or 129 | process that requires permission under the Licensed Rights, such 130 | as reproduction, public display, public performance, distribution, 131 | dissemination, communication, or importation, and to make material 132 | available to the public including in ways that members of the 133 | public may access the material from a place and at a time 134 | individually chosen by them. 135 | 136 | l. Sui Generis Database Rights means rights other than copyright 137 | resulting from Directive 96/9/EC of the European Parliament and of 138 | the Council of 11 March 1996 on the legal protection of databases, 139 | as amended and/or succeeded, as well as other essentially 140 | equivalent rights anywhere in the world. 141 | 142 | m. You means the individual or entity exercising the Licensed Rights 143 | under this Public License. Your has a corresponding meaning. 144 | 145 | 146 | Section 2 -- Scope. 147 | 148 | a. License grant. 149 | 150 | 1. Subject to the terms and conditions of this Public License, 151 | the Licensor hereby grants You a worldwide, royalty-free, 152 | non-sublicensable, non-exclusive, irrevocable license to 153 | exercise the Licensed Rights in the Licensed Material to: 154 | 155 | a. reproduce and Share the Licensed Material, in whole or 156 | in part; and 157 | 158 | b. produce, reproduce, and Share Adapted Material. 159 | 160 | 2. Exceptions and Limitations. For the avoidance of doubt, where 161 | Exceptions and Limitations apply to Your use, this Public 162 | License does not apply, and You do not need to comply with 163 | its terms and conditions. 164 | 165 | 3. Term. The term of this Public License is specified in Section 166 | 6(a). 167 | 168 | 4. Media and formats; technical modifications allowed. The 169 | Licensor authorizes You to exercise the Licensed Rights in 170 | all media and formats whether now known or hereafter created, 171 | and to make technical modifications necessary to do so. The 172 | Licensor waives and/or agrees not to assert any right or 173 | authority to forbid You from making technical modifications 174 | necessary to exercise the Licensed Rights, including 175 | technical modifications necessary to circumvent Effective 176 | Technological Measures. For purposes of this Public License, 177 | simply making modifications authorized by this Section 2(a) 178 | (4) never produces Adapted Material. 179 | 180 | 5. Downstream recipients. 181 | 182 | a. Offer from the Licensor -- Licensed Material. Every 183 | recipient of the Licensed Material automatically 184 | receives an offer from the Licensor to exercise the 185 | Licensed Rights under the terms and conditions of this 186 | Public License. 187 | 188 | b. Additional offer from the Licensor -- Adapted Material. 189 | Every recipient of Adapted Material from You 190 | automatically receives an offer from the Licensor to 191 | exercise the Licensed Rights in the Adapted Material 192 | under the conditions of the Adapter's License You apply. 193 | 194 | c. No downstream restrictions. You may not offer or impose 195 | any additional or different terms or conditions on, or 196 | apply any Effective Technological Measures to, the 197 | Licensed Material if doing so restricts exercise of the 198 | Licensed Rights by any recipient of the Licensed 199 | Material. 200 | 201 | 6. No endorsement. Nothing in this Public License constitutes or 202 | may be construed as permission to assert or imply that You 203 | are, or that Your use of the Licensed Material is, connected 204 | with, or sponsored, endorsed, or granted official status by, 205 | the Licensor or others designated to receive attribution as 206 | provided in Section 3(a)(1)(A)(i). 207 | 208 | b. Other rights. 209 | 210 | 1. Moral rights, such as the right of integrity, are not 211 | licensed under this Public License, nor are publicity, 212 | privacy, and/or other similar personality rights; however, to 213 | the extent possible, the Licensor waives and/or agrees not to 214 | assert any such rights held by the Licensor to the limited 215 | extent necessary to allow You to exercise the Licensed 216 | Rights, but not otherwise. 217 | 218 | 2. Patent and trademark rights are not licensed under this 219 | Public License. 220 | 221 | 3. To the extent possible, the Licensor waives any right to 222 | collect royalties from You for the exercise of the Licensed 223 | Rights, whether directly or through a collecting society 224 | under any voluntary or waivable statutory or compulsory 225 | licensing scheme. In all other cases the Licensor expressly 226 | reserves any right to collect such royalties. 227 | 228 | 229 | Section 3 -- License Conditions. 230 | 231 | Your exercise of the Licensed Rights is expressly made subject to the 232 | following conditions. 233 | 234 | a. Attribution. 235 | 236 | 1. If You Share the Licensed Material (including in modified 237 | form), You must: 238 | 239 | a. retain the following if it is supplied by the Licensor 240 | with the Licensed Material: 241 | 242 | i. identification of the creator(s) of the Licensed 243 | Material and any others designated to receive 244 | attribution, in any reasonable manner requested by 245 | the Licensor (including by pseudonym if 246 | designated); 247 | 248 | ii. a copyright notice; 249 | 250 | iii. a notice that refers to this Public License; 251 | 252 | iv. a notice that refers to the disclaimer of 253 | warranties; 254 | 255 | v. a URI or hyperlink to the Licensed Material to the 256 | extent reasonably practicable; 257 | 258 | b. indicate if You modified the Licensed Material and 259 | retain an indication of any previous modifications; and 260 | 261 | c. indicate the Licensed Material is licensed under this 262 | Public License, and include the text of, or the URI or 263 | hyperlink to, this Public License. 264 | 265 | 2. You may satisfy the conditions in Section 3(a)(1) in any 266 | reasonable manner based on the medium, means, and context in 267 | which You Share the Licensed Material. For example, it may be 268 | reasonable to satisfy the conditions by providing a URI or 269 | hyperlink to a resource that includes the required 270 | information. 271 | 272 | 3. If requested by the Licensor, You must remove any of the 273 | information required by Section 3(a)(1)(A) to the extent 274 | reasonably practicable. 275 | 276 | b. ShareAlike. 277 | 278 | In addition to the conditions in Section 3(a), if You Share 279 | Adapted Material You produce, the following conditions also apply. 280 | 281 | 1. The Adapter's License You apply must be a Creative Commons 282 | license with the same License Elements, this version or 283 | later, or a BY-SA Compatible License. 284 | 285 | 2. You must include the text of, or the URI or hyperlink to, the 286 | Adapter's License You apply. You may satisfy this condition 287 | in any reasonable manner based on the medium, means, and 288 | context in which You Share Adapted Material. 289 | 290 | 3. You may not offer or impose any additional or different terms 291 | or conditions on, or apply any Effective Technological 292 | Measures to, Adapted Material that restrict exercise of the 293 | rights granted under the Adapter's License You apply. 294 | 295 | 296 | Section 4 -- Sui Generis Database Rights. 297 | 298 | Where the Licensed Rights include Sui Generis Database Rights that 299 | apply to Your use of the Licensed Material: 300 | 301 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 302 | to extract, reuse, reproduce, and Share all or a substantial 303 | portion of the contents of the database; 304 | 305 | b. if You include all or a substantial portion of the database 306 | contents in a database in which You have Sui Generis Database 307 | Rights, then the database in which You have Sui Generis Database 308 | Rights (but not its individual contents) is Adapted Material, 309 | 310 | including for purposes of Section 3(b); and 311 | c. You must comply with the conditions in Section 3(a) if You Share 312 | all or a substantial portion of the contents of the database. 313 | 314 | For the avoidance of doubt, this Section 4 supplements and does not 315 | replace Your obligations under this Public License where the Licensed 316 | Rights include other Copyright and Similar Rights. 317 | 318 | 319 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 320 | 321 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 322 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 323 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 324 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 325 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 326 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 327 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 328 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 329 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 330 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 331 | 332 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 333 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 334 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 335 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 336 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 337 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 338 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 339 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 340 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 341 | 342 | c. The disclaimer of warranties and limitation of liability provided 343 | above shall be interpreted in a manner that, to the extent 344 | possible, most closely approximates an absolute disclaimer and 345 | waiver of all liability. 346 | 347 | 348 | Section 6 -- Term and Termination. 349 | 350 | a. This Public License applies for the term of the Copyright and 351 | Similar Rights licensed here. However, if You fail to comply with 352 | this Public License, then Your rights under this Public License 353 | terminate automatically. 354 | 355 | b. Where Your right to use the Licensed Material has terminated under 356 | Section 6(a), it reinstates: 357 | 358 | 1. automatically as of the date the violation is cured, provided 359 | it is cured within 30 days of Your discovery of the 360 | violation; or 361 | 362 | 2. upon express reinstatement by the Licensor. 363 | 364 | For the avoidance of doubt, this Section 6(b) does not affect any 365 | right the Licensor may have to seek remedies for Your violations 366 | of this Public License. 367 | 368 | c. For the avoidance of doubt, the Licensor may also offer the 369 | Licensed Material under separate terms or conditions or stop 370 | distributing the Licensed Material at any time; however, doing so 371 | will not terminate this Public License. 372 | 373 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 374 | License. 375 | 376 | 377 | Section 7 -- Other Terms and Conditions. 378 | 379 | a. The Licensor shall not be bound by any additional or different 380 | terms or conditions communicated by You unless expressly agreed. 381 | 382 | b. Any arrangements, understandings, or agreements regarding the 383 | Licensed Material not stated herein are separate from and 384 | independent of the terms and conditions of this Public License. 385 | 386 | 387 | Section 8 -- Interpretation. 388 | 389 | a. For the avoidance of doubt, this Public License does not, and 390 | shall not be interpreted to, reduce, limit, restrict, or impose 391 | conditions on any use of the Licensed Material that could lawfully 392 | be made without permission under this Public License. 393 | 394 | b. To the extent possible, if any provision of this Public License is 395 | deemed unenforceable, it shall be automatically reformed to the 396 | minimum extent necessary to make it enforceable. If the provision 397 | cannot be reformed, it shall be severed from this Public License 398 | without affecting the enforceability of the remaining terms and 399 | conditions. 400 | 401 | c. No term or condition of this Public License will be waived and no 402 | failure to comply consented to unless expressly agreed to by the 403 | Licensor. 404 | 405 | d. Nothing in this Public License constitutes or may be interpreted 406 | as a limitation upon, or waiver of, any privileges and immunities 407 | that apply to the Licensor or You, including from the legal 408 | processes of any jurisdiction or authority. 409 | 410 | 411 | ======================================================================= 412 | 413 | Creative Commons is not a party to its public licenses. 414 | Notwithstanding, Creative Commons may elect to apply one of its public 415 | licenses to material it publishes and in those instances will be 416 | considered the "Licensor." Except for the limited purpose of indicating 417 | that material is shared under a Creative Commons public license or as 418 | otherwise permitted by the Creative Commons policies published at 419 | creativecommons.org/policies, Creative Commons does not authorize the 420 | use of the trademark "Creative Commons" or any other trademark or logo 421 | of Creative Commons without its prior written consent including, 422 | without limitation, in connection with any unauthorized modifications 423 | to any of its public licenses or any other arrangements, 424 | understandings, or agreements concerning use of licensed material. For 425 | the avoidance of doubt, this paragraph does not form part of the public 426 | licenses. 427 | 428 | Creative Commons may be contacted at creativecommons.org. 429 | -------------------------------------------------------------------------------- /font/material-design-icons/Material-Design-Icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/material-design-icons/Material-Design-Icons.eot -------------------------------------------------------------------------------- /font/material-design-icons/Material-Design-Icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/material-design-icons/Material-Design-Icons.ttf -------------------------------------------------------------------------------- /font/material-design-icons/Material-Design-Icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/material-design-icons/Material-Design-Icons.woff -------------------------------------------------------------------------------- /font/material-design-icons/Material-Design-Icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/material-design-icons/Material-Design-Icons.woff2 -------------------------------------------------------------------------------- /font/roboto/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Bold.eot -------------------------------------------------------------------------------- /font/roboto/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Bold.ttf -------------------------------------------------------------------------------- /font/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /font/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /font/roboto/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Light.eot -------------------------------------------------------------------------------- /font/roboto/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Light.ttf -------------------------------------------------------------------------------- /font/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /font/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /font/roboto/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Medium.eot -------------------------------------------------------------------------------- /font/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /font/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /font/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /font/roboto/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Regular.eot -------------------------------------------------------------------------------- /font/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /font/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /font/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /font/roboto/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Thin.eot -------------------------------------------------------------------------------- /font/roboto/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Thin.ttf -------------------------------------------------------------------------------- /font/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /font/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/font/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Bold.eot -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Bold.ttf -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Light.eot -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Light.ttf -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Medium.eot -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Regular.eot -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Thin.eot -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Thin.ttf -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /fonts/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/fonts/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /img/bouton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/img/bouton.png -------------------------------------------------------------------------------- /img/domoticz-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/img/domoticz-150x150.png -------------------------------------------------------------------------------- /img/france-national-world.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/result_Off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/img/result_Off.png -------------------------------------------------------------------------------- /img/result_On.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AurelienLoyer/domoticz_custom_icon_generator/82f38f272d97203722143da999dc4abf29b5285e/img/result_On.png -------------------------------------------------------------------------------- /img/united_states-national-world.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 🤖 🆒 Domoticz custom icon generator 4 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 22 | 23 | 24 | 28 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
58 | 59 | 65 | 66 |

Domoticz Custom icon generator

67 |
68 | fr flag 74 | en flag 77 |
78 |
79 | 80 |
81 |
82 |
83 | 🤖 🆒 Domoticz custom icon generator, help people to generate custom 84 | icon, resize image, crop image 🔪, and generate zip folder 📦 to have 85 | amazing 🎉 icons in Domoticz app. If you want to contribute to this 86 | project: 87 | Github 🐙 92 | or contact me on 93 | Twitter 🐦 96 | 97 |

98 | Enjoy 🤩 99 |
100 |
101 |
102 | 103 |
104 |
105 |
106 |

1. {{'ICONE_NAME' | translate}}

107 | 108 |
109 |
110 |

2.1 {{'BUTTON_ON' | translate}}

111 | 112 |
113 | {{'SELECT_IMAGE' | translate}}: 114 | 115 |
116 | 117 |
118 | 122 |
123 |
124 |
125 |

2.2 {{'BUTTON_OFF' | translate}}

126 | 127 |
128 | {{'SELECT_IMAGE' | translate}}: 129 | 130 |
131 | 132 |
133 | 137 |
138 |
139 |
140 |
141 |
142 |

{{'PREVIEW_BUTTON_ON' | translate}}

143 | 144 |
145 |
{{gen.iconName}} On
146 |
147 |
151 | 152 |
153 |
154 |
155 |
156 |
157 |

{{'PREVIEW_BUTTON_OFF' | translate}}

158 | 159 |
160 |
{{gen.iconName}} Off
161 |
162 |
166 | 167 |
168 |
169 |
170 |
171 | 172 |
173 |

174 | 3. Image settings 175 |

176 |

177 | Work in progress (Border radius,Border...) 178 |

179 |
180 |
181 |

Bord arrondi

182 | 189 |
192 | 200 |
201 |
202 |

Contour de l'image

203 | 210 |
221 | 230 | {{gen.border}} 231 |
232 |
233 |
234 | 235 |
236 |

4. To finish

237 | 245 |
246 | 247 |
248 |

5. Canvas demo

249 |
250 |
251 |
252 |
253 | 254 |
255 | 266 | 267 | 291 |
292 | 293 | 302 | 303 | 304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /js/app.controller.js: -------------------------------------------------------------------------------- 1 | angular 2 | .module('app', ['ngImgCrop','ngAnimate', 'toastr','pascalprecht.translate']) 3 | .config(function(toastrConfig) { 4 | angular.extend(toastrConfig, { 5 | autoDismiss: false, 6 | containerId: 'toast-container', 7 | maxOpened: 3, 8 | newestOnTop: true, 9 | positionClass: 'toast-bottom-right', 10 | preventDuplicates: false, 11 | preventOpenDuplicates: true, 12 | target: 'body' 13 | }); 14 | }) 15 | .config(['$translateProvider', function ($translateProvider) { 16 | addTranslateConfig($translateProvider); 17 | }]) 18 | .controller('generatorController', generatorController); 19 | 20 | function generatorController($scope,toastr,$translate,$location){ 21 | 22 | var vm = this; 23 | 24 | vm.iconName = ''; 25 | vm.radiusValue = 0; 26 | vm.borderColor = '#7a81ff'; 27 | vm.border = ''; 28 | vm.myImageOn = ''; 29 | vm.myImageOff = ''; 30 | vm.myCroppedImageOn = ''; 31 | vm.myCroppedImageOff = ''; 32 | vm.currentUrl = $location.absUrl(); 33 | vm.local = false; 34 | 35 | vm.radiusChange = radiusChange; 36 | vm.borderChange = borderChange; 37 | vm.zip = zip; 38 | vm.changeLanguage = changeLanguage; 39 | 40 | if(vm.currentUrl.indexOf('localhost') > -1){ 41 | vm.local = true; 42 | } 43 | 44 | let preferedLang = localStorage.getItem('preferedLang'); 45 | if(preferedLang) { 46 | $translate.use(preferedLang); 47 | } 48 | 49 | function changeLanguage(key){ 50 | console.log(key); 51 | $translate.use(key); 52 | localStorage.setItem('preferedLang', key) 53 | } 54 | 55 | function radiusChange() { 56 | if (!vm.activeRadius) { 57 | vm.radiusValue = 0; 58 | } 59 | } 60 | 61 | function borderChange() { 62 | if (vm.activeBorder) { 63 | vm.border = "border:solid "+vm.borderSize+"px "+vm.borderColor+";"; 64 | }else{ 65 | vm.border = ""; 66 | } 67 | } 68 | 69 | var handleFileSelectOn = function(evt) { 70 | var file = evt.currentTarget.files[0]; 71 | var reader = new FileReader(); 72 | reader.onload = function(evt) { 73 | $scope.$apply(function($scope) { 74 | console.log(evt); 75 | vm.myImageOn = evt.target.result; 76 | }); 77 | }; 78 | reader.readAsDataURL(file); 79 | }; 80 | 81 | var handleFileSelectOff = function(evt) { 82 | var file = evt.currentTarget.files[0]; 83 | var reader = new FileReader(); 84 | reader.onload = function(evt) { 85 | $scope.$apply(function($scope) { 86 | console.log(evt); 87 | vm.myImageOff = evt.target.result; 88 | }); 89 | }; 90 | reader.readAsDataURL(file); 91 | }; 92 | 93 | angular.element(document.querySelector('#fileInputOn')).on('change', handleFileSelectOn); 94 | angular.element(document.querySelector('#fileInputOff')).on('change', handleFileSelectOff); 95 | 96 | function zip(){ 97 | 98 | if(!vm.myImageOn){ toastr.error('Image manquante pour le bouton On', 'Erreur'); } 99 | if(!vm.myImageOff){ toastr.error('Image manquante pour le bouton Off', 'Erreur'); } 100 | if(!vm.iconName){ toastr.error('Nom du bouton manquant', 'Erreur'); } 101 | if(!vm.myImageOff || !vm.myImageOn || !vm.iconName){ return; } 102 | 103 | 104 | gtag('event', 'zip', { 105 | 'icon_name': vm.iconName, 106 | }); 107 | 108 | var w = 48; 109 | var h = 48; 110 | 111 | var canvasOn = document.createElement('canvas'); 112 | canvasOn.width = w*2; 113 | canvasOn.height = h*2; 114 | canvasOn.style.width = w + 'px'; 115 | canvasOn.style.height = h + 'px'; 116 | var contextOn = canvasOn.getContext('2d'); 117 | contextOn.scale(2,2); 118 | 119 | var canvasOff = document.createElement('canvas'); 120 | canvasOff.width = w*2; 121 | canvasOff.height = h*2; 122 | canvasOff.style.width = w + 'px'; 123 | canvasOff.style.height = h + 'px'; 124 | var contextOff = canvasOff.getContext('2d'); 125 | contextOff.scale(2,2); 126 | 127 | html2canvas(angular.element(document.querySelector('#imageOnU')), { 128 | canvas: canvasOn, 129 | onrendered: function(canvas) { 130 | $scope.$apply(function($scope) { 131 | angular.element(document.querySelector('#zonetest')).append(canvas); 132 | var imageOn = canvas.toDataURL("image/png"); 133 | html2canvas(angular.element(document.querySelector('#imageOffU')), { 134 | canvas: canvasOff, 135 | onrendered: function(canvas) { 136 | $scope.$apply(function($scope) { 137 | angular.element(document.querySelector('#zonetest')).append(canvas); 138 | var imageOff = canvas.toDataURL("image/png"); 139 | 140 | var zip = new JSZip(); 141 | zip.file("icons.txt", vm.iconName+";Button "+vm.iconName+";Icon "+vm.iconName+" generate via "+vm.currentUrl); 142 | 143 | var savableOn = new Image(); 144 | var savableOff = new Image(); 145 | savableOn.src = imageOn; 146 | savableOff.src = imageOff; 147 | var preview = toDataUrl('../img/bouton.png'); 148 | 149 | zip.file(vm.iconName+"48_On.png", savableOn.src.substr(savableOn.src.indexOf(',')+1), {base64: true}); 150 | zip.file(vm.iconName+"48_Off.png", savableOff.src.substr(savableOff.src.indexOf(',')+1), {base64: true}); 151 | zip.file(vm.iconName+'.png',preview, {base64: true}); 152 | 153 | vm.loader = true; 154 | zip.generateAsync({type:"blob"}).then(function(content) { 155 | vm.loader = false; 156 | saveAs(content, "domoticz_custom_icon_"+vm.iconName+".zip"); 157 | }); 158 | 159 | }) 160 | } 161 | }) 162 | }) 163 | } 164 | }) 165 | 166 | } 167 | } 168 | 169 | function toDataUrl(url, callback, outputFormat){ 170 | var img = new Image(); 171 | img.crossOrigin = 'Anonymous'; 172 | img.onload = function(){ 173 | var canvas = document.createElement('CANVAS'); 174 | var ctx = canvas.getContext('2d'); 175 | var dataURL; 176 | canvas.height = this.height; 177 | canvas.width = this.width; 178 | ctx.drawImage(this, 0, 0); 179 | dataURL = canvas.toDataURL(outputFormat); 180 | //callback(dataURL); 181 | canvas = null; 182 | }; 183 | img.src = url; 184 | } 185 | -------------------------------------------------------------------------------- /js/translate.config.js: -------------------------------------------------------------------------------- 1 | function addTranslateConfig($translateProvider) { 2 | 3 | $translateProvider.preferredLanguage('en'); 4 | 5 | $translateProvider.translations('en', { 6 | DESCRIPTION: `🤖 🆒 Domoticz custom icon generator, help people to generate custom icon, resize image, crop image 🔪, and generate zip folder 📦 to have amazing 🎉 icons in Domoticz app. 7 | If you want to contribute to this project: Github 🐙 or contact me on Twitter 🐦 8 |

Enjoy 🤩`, 9 | ICONE_NAME: 'Icon name', 10 | PREVIEW_BUTTON_ON: 'Preview ON button', 11 | PREVIEW_BUTTON_OFF: 'Preview OFF button', 12 | BUTTON_ON: 'ON Button', 13 | BUTTON_OFF: 'OFF Button', 14 | SELECT_IMAGE: 'Select an image file', 15 | BUTTON_LANG_EN: 'English', 16 | BUTTON_LANG_DE: 'French', 17 | WORK_IN_PROGRESS: 'Work in progress (Border radius,Border...)', 18 | IMAGE_SETTINGS: 'Image settings', 19 | FINISH: 'To finish', 20 | GENERATE_ZIP: `Generate zip`, 21 | CANVAS: `Canvas demo`, 22 | }); 23 | 24 | $translateProvider.translations('fr', { 25 | DESCRIPTION: `🤖 🆒 Domoticz custom icon generator, vous aide à générer des icônes, changer la taille de vos images, recadrer vos images 🔪, et générer un dossier zip 📦 pour avoir des supers icônes 🎉 dans votre application Domoticz. 26 | Si vous voulez contribuer au projet: Github 🐙 ou vous pouvez me contacter sur Twitter 🐦 27 |

Enjoy 🤩`, 28 | ICONE_NAME: `Nom de l'icone`, 29 | PREVIEW_BUTTON_ON: 'Aperçu du bouton ON', 30 | PREVIEW_BUTTON_OFF: 'Aperçu du bouton OFF', 31 | BUTTON_ON: 'Bouton ON', 32 | BUTTON_OFF: 'Bouton OFF', 33 | SELECT_IMAGE: 'Sélectionner un fichier image', 34 | BUTTON_LANG_EN: 'Englais', 35 | BUTTON_LANG_DE: 'Français', 36 | IMAGE_SETTINGS: `Réglage de l'image`, 37 | WORK_IN_PROGRESS: 'En développement (Bord arrondi,Bordure...)', 38 | FINISH: 'Pour finir', 39 | GENERATE_ZIP: `Générer le zip`, 40 | CANVAS: `Rendu canvas`, 41 | }); 42 | } -------------------------------------------------------------------------------- /lib/FileSaver.js: -------------------------------------------------------------------------------- 1 | /*! FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 2014-01-24 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * License: X11/MIT 7 | * See LICENSE.md 8 | */ 9 | 10 | /*global self */ 11 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 12 | 13 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ 14 | 15 | var saveAs = saveAs 16 | // IE 10+ (native saveAs) 17 | || (typeof navigator !== "undefined" && 18 | navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) 19 | // Everyone else 20 | || (function(view) { 21 | "use strict"; 22 | // IE <10 is explicitly unsupported 23 | if (typeof navigator !== "undefined" && 24 | /MSIE [1-9]\./.test(navigator.userAgent)) { 25 | return; 26 | } 27 | var 28 | doc = view.document 29 | // only get URL when necessary in case BlobBuilder.js hasn't overridden it yet 30 | , get_URL = function() { 31 | return view.URL || view.webkitURL || view; 32 | } 33 | , URL = view.URL || view.webkitURL || view 34 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 35 | , can_use_save_link = !view.externalHost && "download" in save_link 36 | , click = function(node) { 37 | var event = doc.createEvent("MouseEvents"); 38 | event.initMouseEvent( 39 | "click", true, false, view, 0, 0, 0, 0, 0 40 | , false, false, false, false, 0, null 41 | ); 42 | node.dispatchEvent(event); 43 | } 44 | , webkit_req_fs = view.webkitRequestFileSystem 45 | , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem 46 | , throw_outside = function(ex) { 47 | (view.setImmediate || view.setTimeout)(function() { 48 | throw ex; 49 | }, 0); 50 | } 51 | , force_saveable_type = "application/octet-stream" 52 | , fs_min_size = 0 53 | , deletion_queue = [] 54 | , process_deletion_queue = function() { 55 | var i = deletion_queue.length; 56 | while (i--) { 57 | var file = deletion_queue[i]; 58 | if (typeof file === "string") { // file is an object URL 59 | URL.revokeObjectURL(file); 60 | } else { // file is a File 61 | file.remove(); 62 | } 63 | } 64 | deletion_queue.length = 0; // clear queue 65 | } 66 | , dispatch = function(filesaver, event_types, event) { 67 | event_types = [].concat(event_types); 68 | var i = event_types.length; 69 | while (i--) { 70 | var listener = filesaver["on" + event_types[i]]; 71 | if (typeof listener === "function") { 72 | try { 73 | listener.call(filesaver, event || filesaver); 74 | } catch (ex) { 75 | throw_outside(ex); 76 | } 77 | } 78 | } 79 | } 80 | , FileSaver = function(blob, name) { 81 | // First try a.download, then web filesystem, then object URLs 82 | var 83 | filesaver = this 84 | , type = blob.type 85 | , blob_changed = false 86 | , object_url 87 | , target_view 88 | , get_object_url = function() { 89 | var object_url = get_URL().createObjectURL(blob); 90 | deletion_queue.push(object_url); 91 | return object_url; 92 | } 93 | , dispatch_all = function() { 94 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 95 | } 96 | // on any filesys errors revert to saving with object URLs 97 | , fs_error = function() { 98 | // don't create more object URLs than needed 99 | if (blob_changed || !object_url) { 100 | object_url = get_object_url(blob); 101 | } 102 | if (target_view) { 103 | target_view.location.href = object_url; 104 | } else { 105 | window.open(object_url, "_blank"); 106 | } 107 | filesaver.readyState = filesaver.DONE; 108 | dispatch_all(); 109 | } 110 | , abortable = function(func) { 111 | return function() { 112 | if (filesaver.readyState !== filesaver.DONE) { 113 | return func.apply(this, arguments); 114 | } 115 | }; 116 | } 117 | , create_if_not_found = {create: true, exclusive: false} 118 | , slice 119 | ; 120 | filesaver.readyState = filesaver.INIT; 121 | if (!name) { 122 | name = "download"; 123 | } 124 | if (can_use_save_link) { 125 | object_url = get_object_url(blob); 126 | // FF for Android has a nasty garbage collection mechanism 127 | // that turns all objects that are not pure javascript into 'deadObject' 128 | // this means `doc` and `save_link` are unusable and need to be recreated 129 | // `view` is usable though: 130 | doc = view.document; 131 | save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a"); 132 | save_link.href = object_url; 133 | save_link.download = name; 134 | var event = doc.createEvent("MouseEvents"); 135 | event.initMouseEvent( 136 | "click", true, false, view, 0, 0, 0, 0, 0 137 | , false, false, false, false, 0, null 138 | ); 139 | save_link.dispatchEvent(event); 140 | filesaver.readyState = filesaver.DONE; 141 | dispatch_all(); 142 | return; 143 | } 144 | // Object and web filesystem URLs have a problem saving in Google Chrome when 145 | // viewed in a tab, so I force save with application/octet-stream 146 | // http://code.google.com/p/chromium/issues/detail?id=91158 147 | if (view.chrome && type && type !== force_saveable_type) { 148 | slice = blob.slice || blob.webkitSlice; 149 | blob = slice.call(blob, 0, blob.size, force_saveable_type); 150 | blob_changed = true; 151 | } 152 | // Since I can't be sure that the guessed media type will trigger a download 153 | // in WebKit, I append .download to the filename. 154 | // https://bugs.webkit.org/show_bug.cgi?id=65440 155 | if (webkit_req_fs && name !== "download") { 156 | name += ".download"; 157 | } 158 | if (type === force_saveable_type || webkit_req_fs) { 159 | target_view = view; 160 | } 161 | if (!req_fs) { 162 | fs_error(); 163 | return; 164 | } 165 | fs_min_size += blob.size; 166 | req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { 167 | fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { 168 | var save = function() { 169 | dir.getFile(name, create_if_not_found, abortable(function(file) { 170 | file.createWriter(abortable(function(writer) { 171 | writer.onwriteend = function(event) { 172 | target_view.location.href = file.toURL(); 173 | deletion_queue.push(file); 174 | filesaver.readyState = filesaver.DONE; 175 | dispatch(filesaver, "writeend", event); 176 | }; 177 | writer.onerror = function() { 178 | var error = writer.error; 179 | if (error.code !== error.ABORT_ERR) { 180 | fs_error(); 181 | } 182 | }; 183 | "writestart progress write abort".split(" ").forEach(function(event) { 184 | writer["on" + event] = filesaver["on" + event]; 185 | }); 186 | writer.write(blob); 187 | filesaver.abort = function() { 188 | writer.abort(); 189 | filesaver.readyState = filesaver.DONE; 190 | }; 191 | filesaver.readyState = filesaver.WRITING; 192 | }), fs_error); 193 | }), fs_error); 194 | }; 195 | dir.getFile(name, {create: false}, abortable(function(file) { 196 | // delete file if it already exists 197 | file.remove(); 198 | save(); 199 | }), abortable(function(ex) { 200 | if (ex.code === ex.NOT_FOUND_ERR) { 201 | save(); 202 | } else { 203 | fs_error(); 204 | } 205 | })); 206 | }), fs_error); 207 | }), fs_error); 208 | } 209 | , FS_proto = FileSaver.prototype 210 | , saveAs = function(blob, name) { 211 | return new FileSaver(blob, name); 212 | } 213 | ; 214 | FS_proto.abort = function() { 215 | var filesaver = this; 216 | filesaver.readyState = filesaver.DONE; 217 | dispatch(filesaver, "abort"); 218 | }; 219 | FS_proto.readyState = FS_proto.INIT = 0; 220 | FS_proto.WRITING = 1; 221 | FS_proto.DONE = 2; 222 | 223 | FS_proto.error = 224 | FS_proto.onwritestart = 225 | FS_proto.onprogress = 226 | FS_proto.onwrite = 227 | FS_proto.onabort = 228 | FS_proto.onerror = 229 | FS_proto.onwriteend = 230 | null; 231 | 232 | view.addEventListener("unload", process_deletion_queue, false); 233 | saveAs.unload = function() { 234 | process_deletion_queue(); 235 | view.removeEventListener("unload", process_deletion_queue, false); 236 | }; 237 | return saveAs; 238 | }( 239 | typeof self !== "undefined" && self 240 | || typeof window !== "undefined" && window 241 | || this.content 242 | )); 243 | // `self` is undefined in Firefox for Android content script context 244 | // while `this` is nsIContentFrameMessageManager 245 | // with an attribute `content` that corresponds to the window 246 | 247 | if (typeof module !== "undefined") module.exports = saveAs; 248 | -------------------------------------------------------------------------------- /lib/angular-toastr.tpls.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | angular.module('toastr', []) 5 | .factory('toastr', toastr); 6 | 7 | toastr.$inject = ['$animate', '$injector', '$document', '$rootScope', '$sce', 'toastrConfig', '$q']; 8 | 9 | function toastr($animate, $injector, $document, $rootScope, $sce, toastrConfig, $q) { 10 | var container; 11 | var index = 0; 12 | var toasts = []; 13 | 14 | var previousToastMessage = ''; 15 | var openToasts = {}; 16 | 17 | var containerDefer = $q.defer(); 18 | 19 | var toast = { 20 | active: active, 21 | clear: clear, 22 | error: error, 23 | info: info, 24 | remove: remove, 25 | success: success, 26 | warning: warning 27 | }; 28 | 29 | return toast; 30 | 31 | /* Public API */ 32 | function active() { 33 | return toasts.length; 34 | } 35 | 36 | function clear(toast) { 37 | // Bit of a hack, I will remove this soon with a BC 38 | if (arguments.length === 1 && !toast) { return; } 39 | 40 | if (toast) { 41 | remove(toast.toastId); 42 | } else { 43 | for (var i = 0; i < toasts.length; i++) { 44 | remove(toasts[i].toastId); 45 | } 46 | } 47 | } 48 | 49 | function error(message, title, optionsOverride) { 50 | var type = _getOptions().iconClasses.error; 51 | return _buildNotification(type, message, title, optionsOverride); 52 | } 53 | 54 | function info(message, title, optionsOverride) { 55 | var type = _getOptions().iconClasses.info; 56 | return _buildNotification(type, message, title, optionsOverride); 57 | } 58 | 59 | function success(message, title, optionsOverride) { 60 | var type = _getOptions().iconClasses.success; 61 | return _buildNotification(type, message, title, optionsOverride); 62 | } 63 | 64 | function warning(message, title, optionsOverride) { 65 | var type = _getOptions().iconClasses.warning; 66 | return _buildNotification(type, message, title, optionsOverride); 67 | } 68 | 69 | function remove(toastId, wasClicked) { 70 | var toast = findToast(toastId); 71 | 72 | if (toast && ! toast.deleting) { // Avoid clicking when fading out 73 | toast.deleting = true; 74 | toast.isOpened = false; 75 | $animate.leave(toast.el).then(function() { 76 | if (toast.scope.options.onHidden) { 77 | toast.scope.options.onHidden(!!wasClicked, toast); 78 | } 79 | toast.scope.$destroy(); 80 | var index = toasts.indexOf(toast); 81 | delete openToasts[toast.scope.message]; 82 | toasts.splice(index, 1); 83 | var maxOpened = toastrConfig.maxOpened; 84 | if (maxOpened && toasts.length >= maxOpened) { 85 | toasts[maxOpened - 1].open.resolve(); 86 | } 87 | if (lastToast()) { 88 | container.remove(); 89 | container = null; 90 | containerDefer = $q.defer(); 91 | } 92 | }); 93 | } 94 | 95 | function findToast(toastId) { 96 | for (var i = 0; i < toasts.length; i++) { 97 | if (toasts[i].toastId === toastId) { 98 | return toasts[i]; 99 | } 100 | } 101 | } 102 | 103 | function lastToast() { 104 | return !toasts.length; 105 | } 106 | } 107 | 108 | /* Internal functions */ 109 | function _buildNotification(type, message, title, optionsOverride) { 110 | if (angular.isObject(title)) { 111 | optionsOverride = title; 112 | title = null; 113 | } 114 | 115 | return _notify({ 116 | iconClass: type, 117 | message: message, 118 | optionsOverride: optionsOverride, 119 | title: title 120 | }); 121 | } 122 | 123 | function _getOptions() { 124 | return angular.extend({}, toastrConfig); 125 | } 126 | 127 | function _createOrGetContainer(options) { 128 | if(container) { return containerDefer.promise; } 129 | 130 | container = angular.element('
'); 131 | container.attr('id', options.containerId); 132 | container.addClass(options.positionClass); 133 | container.css({'pointer-events': 'auto'}); 134 | 135 | var target = angular.element(document.querySelector(options.target)); 136 | 137 | if ( ! target || ! target.length) { 138 | throw 'Target for toasts doesn\'t exist'; 139 | } 140 | 141 | $animate.enter(container, target).then(function() { 142 | containerDefer.resolve(); 143 | }); 144 | 145 | return containerDefer.promise; 146 | } 147 | 148 | function _notify(map) { 149 | var options = _getOptions(); 150 | 151 | if (shouldExit()) { return; } 152 | 153 | var newToast = createToast(); 154 | 155 | toasts.push(newToast); 156 | 157 | if (ifMaxOpenedAndAutoDismiss()) { 158 | var oldToasts = toasts.slice(0, (toasts.length - options.maxOpened)); 159 | for (var i = 0, len = oldToasts.length; i < len; i++) { 160 | remove(oldToasts[i].toastId); 161 | } 162 | } 163 | 164 | if (maxOpenedNotReached()) { 165 | newToast.open.resolve(); 166 | } 167 | 168 | newToast.open.promise.then(function() { 169 | _createOrGetContainer(options).then(function() { 170 | newToast.isOpened = true; 171 | if (options.newestOnTop) { 172 | $animate.enter(newToast.el, container).then(function() { 173 | newToast.scope.init(); 174 | }); 175 | } else { 176 | var sibling = container[0].lastChild ? angular.element(container[0].lastChild) : null; 177 | $animate.enter(newToast.el, container, sibling).then(function() { 178 | newToast.scope.init(); 179 | }); 180 | } 181 | }); 182 | }); 183 | 184 | return newToast; 185 | 186 | function ifMaxOpenedAndAutoDismiss() { 187 | return options.autoDismiss && options.maxOpened && toasts.length > options.maxOpened; 188 | } 189 | 190 | function createScope(toast, map, options) { 191 | if (options.allowHtml) { 192 | toast.scope.allowHtml = true; 193 | toast.scope.title = $sce.trustAsHtml(map.title); 194 | toast.scope.message = $sce.trustAsHtml(map.message); 195 | } else { 196 | toast.scope.title = map.title; 197 | toast.scope.message = map.message; 198 | } 199 | 200 | toast.scope.toastType = toast.iconClass; 201 | toast.scope.toastId = toast.toastId; 202 | toast.scope.extraData = options.extraData; 203 | 204 | toast.scope.options = { 205 | extendedTimeOut: options.extendedTimeOut, 206 | messageClass: options.messageClass, 207 | onHidden: options.onHidden, 208 | onShown: generateEvent('onShown'), 209 | onTap: generateEvent('onTap'), 210 | progressBar: options.progressBar, 211 | tapToDismiss: options.tapToDismiss, 212 | timeOut: options.timeOut, 213 | titleClass: options.titleClass, 214 | toastClass: options.toastClass 215 | }; 216 | 217 | if (options.closeButton) { 218 | toast.scope.options.closeHtml = options.closeHtml; 219 | } 220 | 221 | function generateEvent(event) { 222 | if (options[event]) { 223 | return function() { 224 | options[event](toast); 225 | }; 226 | } 227 | } 228 | } 229 | 230 | function createToast() { 231 | var newToast = { 232 | toastId: index++, 233 | isOpened: false, 234 | scope: $rootScope.$new(), 235 | open: $q.defer() 236 | }; 237 | newToast.iconClass = map.iconClass; 238 | if (map.optionsOverride) { 239 | angular.extend(options, cleanOptionsOverride(map.optionsOverride)); 240 | newToast.iconClass = map.optionsOverride.iconClass || newToast.iconClass; 241 | } 242 | 243 | createScope(newToast, map, options); 244 | 245 | newToast.el = createToastEl(newToast.scope); 246 | 247 | return newToast; 248 | 249 | function cleanOptionsOverride(options) { 250 | var badOptions = ['containerId', 'iconClasses', 'maxOpened', 'newestOnTop', 251 | 'positionClass', 'preventDuplicates', 'preventOpenDuplicates', 'templates']; 252 | for (var i = 0, l = badOptions.length; i < l; i++) { 253 | delete options[badOptions[i]]; 254 | } 255 | 256 | return options; 257 | } 258 | } 259 | 260 | function createToastEl(scope) { 261 | var angularDomEl = angular.element('
'), 262 | $compile = $injector.get('$compile'); 263 | return $compile(angularDomEl)(scope); 264 | } 265 | 266 | function maxOpenedNotReached() { 267 | return options.maxOpened && toasts.length <= options.maxOpened || !options.maxOpened; 268 | } 269 | 270 | function shouldExit() { 271 | var isDuplicateOfLast = options.preventDuplicates && map.message === previousToastMessage; 272 | var isDuplicateOpen = options.preventOpenDuplicates && openToasts[map.message]; 273 | 274 | if (isDuplicateOfLast || isDuplicateOpen) { 275 | return true; 276 | } 277 | 278 | previousToastMessage = map.message; 279 | openToasts[map.message] = true; 280 | 281 | return false; 282 | } 283 | } 284 | } 285 | }()); 286 | 287 | (function() { 288 | 'use strict'; 289 | 290 | angular.module('toastr') 291 | .constant('toastrConfig', { 292 | allowHtml: false, 293 | autoDismiss: false, 294 | closeButton: false, 295 | closeHtml: '', 296 | containerId: 'toast-container', 297 | extendedTimeOut: 1000, 298 | iconClasses: { 299 | error: 'toast-error', 300 | info: 'toast-info', 301 | success: 'toast-success', 302 | warning: 'toast-warning' 303 | }, 304 | maxOpened: 0, 305 | messageClass: 'toast-message', 306 | newestOnTop: true, 307 | onHidden: null, 308 | onShown: null, 309 | onTap: null, 310 | positionClass: 'toast-top-right', 311 | preventDuplicates: false, 312 | preventOpenDuplicates: false, 313 | progressBar: false, 314 | tapToDismiss: true, 315 | target: 'body', 316 | templates: { 317 | toast: 'directives/toast/toast.html', 318 | progressbar: 'directives/progressbar/progressbar.html' 319 | }, 320 | timeOut: 5000, 321 | titleClass: 'toast-title', 322 | toastClass: 'toast' 323 | }); 324 | }()); 325 | 326 | (function() { 327 | 'use strict'; 328 | 329 | angular.module('toastr') 330 | .directive('progressBar', progressBar); 331 | 332 | progressBar.$inject = ['toastrConfig']; 333 | 334 | function progressBar(toastrConfig) { 335 | return { 336 | replace: true, 337 | require: '^toast', 338 | templateUrl: function() { 339 | return toastrConfig.templates.progressbar; 340 | }, 341 | link: linkFunction 342 | }; 343 | 344 | function linkFunction(scope, element, attrs, toastCtrl) { 345 | var intervalId, currentTimeOut, hideTime; 346 | 347 | toastCtrl.progressBar = scope; 348 | 349 | scope.start = function(duration) { 350 | if (intervalId) { 351 | clearInterval(intervalId); 352 | } 353 | 354 | currentTimeOut = parseFloat(duration); 355 | hideTime = new Date().getTime() + currentTimeOut; 356 | intervalId = setInterval(updateProgress, 10); 357 | }; 358 | 359 | scope.stop = function() { 360 | if (intervalId) { 361 | clearInterval(intervalId); 362 | } 363 | }; 364 | 365 | function updateProgress() { 366 | var percentage = ((hideTime - (new Date().getTime())) / currentTimeOut) * 100; 367 | element.css('width', percentage + '%'); 368 | } 369 | 370 | scope.$on('$destroy', function() { 371 | // Failsafe stop 372 | clearInterval(intervalId); 373 | }); 374 | } 375 | } 376 | }()); 377 | 378 | (function() { 379 | 'use strict'; 380 | 381 | angular.module('toastr') 382 | .controller('ToastController', ToastController); 383 | 384 | function ToastController() { 385 | this.progressBar = null; 386 | 387 | this.startProgressBar = function(duration) { 388 | if (this.progressBar) { 389 | this.progressBar.start(duration); 390 | } 391 | }; 392 | 393 | this.stopProgressBar = function() { 394 | if (this.progressBar) { 395 | this.progressBar.stop(); 396 | } 397 | }; 398 | } 399 | }()); 400 | 401 | (function() { 402 | 'use strict'; 403 | 404 | angular.module('toastr') 405 | .directive('toast', toast); 406 | 407 | toast.$inject = ['$injector', '$interval', 'toastrConfig', 'toastr']; 408 | 409 | function toast($injector, $interval, toastrConfig, toastr) { 410 | return { 411 | replace: true, 412 | templateUrl: function() { 413 | return toastrConfig.templates.toast; 414 | }, 415 | controller: 'ToastController', 416 | link: toastLinkFunction 417 | }; 418 | 419 | function toastLinkFunction(scope, element, attrs, toastCtrl) { 420 | var timeout; 421 | 422 | scope.toastClass = scope.options.toastClass; 423 | scope.titleClass = scope.options.titleClass; 424 | scope.messageClass = scope.options.messageClass; 425 | scope.progressBar = scope.options.progressBar; 426 | 427 | if (wantsCloseButton()) { 428 | var button = angular.element(scope.options.closeHtml), 429 | $compile = $injector.get('$compile'); 430 | button.addClass('toast-close-button'); 431 | button.attr('ng-click', 'close(true, $event)'); 432 | $compile(button)(scope); 433 | element.prepend(button); 434 | } 435 | 436 | scope.init = function() { 437 | if (scope.options.timeOut) { 438 | timeout = createTimeout(scope.options.timeOut); 439 | } 440 | if (scope.options.onShown) { 441 | scope.options.onShown(); 442 | } 443 | }; 444 | 445 | element.on('mouseenter', function() { 446 | hideAndStopProgressBar(); 447 | if (timeout) { 448 | $interval.cancel(timeout); 449 | } 450 | }); 451 | 452 | scope.tapToast = function () { 453 | if (angular.isFunction(scope.options.onTap)) { 454 | scope.options.onTap(); 455 | } 456 | if (scope.options.tapToDismiss) { 457 | scope.close(true); 458 | } 459 | }; 460 | 461 | scope.close = function (wasClicked, $event) { 462 | if ($event && angular.isFunction($event.stopPropagation)) { 463 | $event.stopPropagation(); 464 | } 465 | toastr.remove(scope.toastId, wasClicked); 466 | }; 467 | 468 | element.on('mouseleave', function() { 469 | if (scope.options.timeOut === 0 && scope.options.extendedTimeOut === 0) { return; } 470 | scope.$apply(function() { 471 | scope.progressBar = scope.options.progressBar; 472 | }); 473 | timeout = createTimeout(scope.options.extendedTimeOut); 474 | }); 475 | 476 | function createTimeout(time) { 477 | toastCtrl.startProgressBar(time); 478 | return $interval(function() { 479 | toastCtrl.stopProgressBar(); 480 | toastr.remove(scope.toastId); 481 | }, time, 1); 482 | } 483 | 484 | function hideAndStopProgressBar() { 485 | scope.progressBar = false; 486 | toastCtrl.stopProgressBar(); 487 | } 488 | 489 | function wantsCloseButton() { 490 | return scope.options.closeHtml; 491 | } 492 | } 493 | } 494 | }()); 495 | 496 | angular.module("toastr").run(["$templateCache", function($templateCache) {$templateCache.put("directives/progressbar/progressbar.html","
\n"); 497 | $templateCache.put("directives/toast/toast.html","
\n
\n
{{title}}
\n
{{message}}
\n
\n
\n
\n \n
\n");}]); -------------------------------------------------------------------------------- /lib/angular-translate.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-translate - v2.9.0 - 2016-01-24 3 | * 4 | * Copyright (c) 2016 The angular-translate team, Pascal Precht; Licensed MIT 5 | */ 6 | !function(a,b){"function"==typeof define&&define.amd?define([],function(){return b()}):"object"==typeof exports?module.exports=b():b()}(this,function(){function a(a){"use strict";var b=a.storageKey(),c=a.storage(),d=function(){var d=a.preferredLanguage();angular.isString(d)?a.use(d):c.put(b,a.use())};d.displayName="fallbackFromIncorrectStorageValue",c?c.get(b)?a.use(c.get(b))["catch"](d):d():angular.isString(a.preferredLanguage())&&a.use(a.preferredLanguage())}function b(){"use strict";var a,b,c=null,d=!1,e=!1;b={sanitize:function(a,b){return"text"===b&&(a=g(a)),a},escape:function(a,b){return"text"===b&&(a=f(a)),a},sanitizeParameters:function(a,b){return"params"===b&&(a=h(a,g)),a},escapeParameters:function(a,b){return"params"===b&&(a=h(a,f)),a}},b.escaped=b.escapeParameters,this.addStrategy=function(a,c){return b[a]=c,this},this.removeStrategy=function(a){return delete b[a],this},this.useStrategy=function(a){return d=!0,c=a,this},this.$get=["$injector","$log",function(f,g){var h={},i=function(a,c,d){return angular.forEach(d,function(d){if(angular.isFunction(d))a=d(a,c);else if(angular.isFunction(b[d]))a=b[d](a,c);else{if(!angular.isString(b[d]))throw new Error("pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: '"+d+"'");if(!h[b[d]])try{h[b[d]]=f.get(b[d])}catch(e){throw h[b[d]]=function(){},new Error("pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: '"+d+"'")}a=h[b[d]](a,c)}}),a},j=function(){d||e||(g.warn("pascalprecht.translate.$translateSanitization: No sanitization strategy has been configured. This can have serious security implications. See http://angular-translate.github.io/docs/#/guide/19_security for details."),e=!0)};return f.has("$sanitize")&&(a=f.get("$sanitize")),{useStrategy:function(a){return function(b){a.useStrategy(b)}}(this),sanitize:function(a,b,d){if(c||j(),arguments.length<3&&(d=c),!d)return a;var e=angular.isArray(d)?d:[d];return i(a,b,e)}}}];var f=function(a){var b=angular.element("
");return b.text(a),b.html()},g=function(b){if(!a)throw new Error("pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as 'escape'.");return a(b)},h=function(a,b){if(angular.isObject(a)){var c=angular.isArray(a)?[]:{};return angular.forEach(a,function(a,d){c[d]=h(a,b)}),c}return angular.isNumber(a)?a:b(a)}}function c(a,b,c,d){"use strict";var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t={},u=[],v=a,w=[],x="translate-cloak",y=!1,z=!1,A=".",B=!1,C=0,D=!0,E="default",F={"default":function(a){return(a||"").split("-").join("_")},java:function(a){var b=(a||"").split("-").join("_"),c=b.split("_");return c.length>1?c[0].toLowerCase()+"_"+c[1].toUpperCase():b},bcp47:function(a){var b=(a||"").split("_").join("-"),c=b.split("-");return c.length>1?c[0].toLowerCase()+"-"+c[1].toUpperCase():b}},G="2.9.0",H=function(){if(angular.isFunction(d.getLocale))return d.getLocale();var a,c,e=b.$get().navigator,f=["language","browserLanguage","systemLanguage","userLanguage"];if(angular.isArray(e.languages))for(a=0;ac;c++)if(a[c]===b)return c;return-1},K=function(){return this.toString().replace(/^\s+|\s+$/g,"")},L=function(a){if(a){for(var b=[],c=angular.lowercase(a),d=0,e=u.length;e>d;d++)b.push(angular.lowercase(u[d]));if(J(b,c)>-1)return a;if(f){var g;for(var h in f){var i=!1,j=Object.prototype.hasOwnProperty.call(f,h)&&angular.lowercase(h)===angular.lowercase(a);if("*"===h.slice(-1)&&(i=h.slice(0,-1)===a.slice(0,h.length-1)),(j||i)&&(g=f[h],J(b,angular.lowercase(g))>-1))return g}}var k=a.split("_");return k.length>1&&J(b,angular.lowercase(k[0]))>-1?k[0]:void 0}},M=function(a,b){if(!a&&!b)return t;if(a&&!b){if(angular.isString(a))return t[a]}else angular.isObject(t[a])||(t[a]={}),angular.extend(t[a],N(b));return this};this.translations=M,this.cloakClassName=function(a){return a?(x=a,this):x},this.nestedObjectDelimeter=function(a){return a?(A=a,this):A};var N=function(a,b,c,d){var e,f,g,h;b||(b=[]),c||(c={});for(e in a)Object.prototype.hasOwnProperty.call(a,e)&&(h=a[e],angular.isObject(h)?N(h,b.concat(e),c,e):(f=b.length?""+b.join(A)+A+e:e,b.length&&e===d&&(g=""+b.join(A),c[g]="@:"+f),c[f]=h));return c};N.displayName="flatObject",this.addInterpolation=function(a){return w.push(a),this},this.useMessageFormatInterpolation=function(){return this.useInterpolation("$translateMessageFormatInterpolation")},this.useInterpolation=function(a){return n=a,this},this.useSanitizeValueStrategy=function(a){return c.useStrategy(a),this},this.preferredLanguage=function(a){return a?(O(a),this):e};var O=function(a){return a&&(e=a),e};this.translationNotFoundIndicator=function(a){return this.translationNotFoundIndicatorLeft(a),this.translationNotFoundIndicatorRight(a),this},this.translationNotFoundIndicatorLeft=function(a){return a?(q=a,this):q},this.translationNotFoundIndicatorRight=function(a){return a?(r=a,this):r},this.fallbackLanguage=function(a){return P(a),this};var P=function(a){return a?(angular.isString(a)?(h=!0,g=[a]):angular.isArray(a)&&(h=!1,g=a),angular.isString(e)&&J(g,e)<0&&g.push(e),this):h?g[0]:g};this.use=function(a){if(a){if(!t[a]&&!o)throw new Error("$translateProvider couldn't find translationTable for langKey: '"+a+"'");return i=a,this}return i};var Q=function(a){return a?(v=a,this):l?l+v:v};this.storageKey=Q,this.useUrlLoader=function(a,b){return this.useLoader("$translateUrlLoader",angular.extend({url:a},b))},this.useStaticFilesLoader=function(a){return this.useLoader("$translateStaticFilesLoader",a)},this.useLoader=function(a,b){return o=a,p=b||{},this},this.useLocalStorage=function(){return this.useStorage("$translateLocalStorage")},this.useCookieStorage=function(){return this.useStorage("$translateCookieStorage")},this.useStorage=function(a){return k=a,this},this.storagePrefix=function(a){return a?(l=a,this):a},this.useMissingTranslationHandlerLog=function(){return this.useMissingTranslationHandler("$translateMissingTranslationHandlerLog")},this.useMissingTranslationHandler=function(a){return m=a,this},this.usePostCompiling=function(a){return y=!!a,this},this.forceAsyncReload=function(a){return z=!!a,this},this.uniformLanguageTag=function(a){return a?angular.isString(a)&&(a={standard:a}):a={},E=a.standard,this},this.determinePreferredLanguage=function(a){var b=a&&angular.isFunction(a)?a():I();return e=u.length?L(b)||b:b,this},this.registerAvailableLanguageKeys=function(a,b){return a?(u=a,b&&(f=b),this):u},this.useLoaderCache=function(a){return a===!1?s=void 0:a===!0?s=!0:"undefined"==typeof a?s="$translationCache":a&&(s=a),this},this.directivePriority=function(a){return void 0===a?C:(C=a,this)},this.statefulFilter=function(a){return void 0===a?D:(D=a,this)},this.$get=["$log","$injector","$rootScope","$q",function(a,b,c,d){var f,l,u,E=b.get(n||"$translateDefaultInterpolation"),F=!1,H={},I={},R=function(a,b,c,h,j){var m=j&&j!==i?L(j)||j:i;if(angular.isArray(a)){var n=function(a){for(var e={},f=[],g=function(a){var f=d.defer(),g=function(b){e[a]=b,f.resolve([a,b])};return R(a,b,c,h,j).then(g,g),f.promise},i=0,k=a.length;k>i;i++)f.push(g(a[i]));return d.all(f).then(function(){return e})};return n(a)}var o=d.defer();a&&(a=K.apply(a));var p=function(){var a=e?I[e]:I[m];if(l=0,k&&!a){var b=f.get(v);if(a=I[b],g&&g.length){var c=J(g,b);l=0===c?1:0,J(g,e)<0&&g.push(e)}}return a}();if(p){var q=function(){j||(m=i),ca(a,b,c,h,m).then(o.resolve,o.reject)};q.displayName="promiseResolved",p["finally"](q,o.reject)}else ca(a,b,c,h,m).then(o.resolve,o.reject);return o.promise},S=function(a){return q&&(a=[q,a].join(" ")),r&&(a=[a,r].join(" ")),a},T=function(a){i=a,k&&f.put(R.storageKey(),i),c.$emit("$translateChangeSuccess",{language:a}),E.setLocale(i);var b=function(a,b){H[b].setLocale(i)};b.displayName="eachInterpolatorLocaleSetter",angular.forEach(H,b),c.$emit("$translateChangeEnd",{language:a})},U=function(a){if(!a)throw"No language key specified for loading.";var e=d.defer();c.$emit("$translateLoadingStart",{language:a}),F=!0;var f=s;"string"==typeof f&&(f=b.get(f));var g=angular.extend({},p,{key:a,$http:angular.extend({},{cache:f},p.$http)}),h=function(b){var d={};c.$emit("$translateLoadingSuccess",{language:a}),angular.isArray(b)?angular.forEach(b,function(a){angular.extend(d,N(a))}):angular.extend(d,N(b)),F=!1,e.resolve({key:a,table:d}),c.$emit("$translateLoadingEnd",{language:a})};h.displayName="onLoaderSuccess";var i=function(a){c.$emit("$translateLoadingError",{language:a}),e.reject(a),c.$emit("$translateLoadingEnd",{language:a})};return i.displayName="onLoaderError",b.get(o)(g).then(h,i),e.promise};if(k&&(f=b.get(k),!f.get||!f.put))throw new Error("Couldn't use storage '"+k+"', missing get() or put() method!");if(w.length){var V=function(a){var c=b.get(a);c.setLocale(e||i),H[c.getInterpolationIdentifier()]=c};V.displayName="interpolationFactoryAdder",angular.forEach(w,V)}var W=function(a){var b=d.defer();if(Object.prototype.hasOwnProperty.call(t,a))b.resolve(t[a]);else if(I[a]){var c=function(a){M(a.key,a.table),b.resolve(a.table)};c.displayName="translationTableResolver",I[a].then(c,b.reject)}else b.reject();return b.promise},X=function(a,b,c,e){var f=d.defer(),g=function(d){if(Object.prototype.hasOwnProperty.call(d,b)){e.setLocale(a);var g=d[b];"@:"===g.substr(0,2)?X(a,g.substr(2),c,e).then(f.resolve,f.reject):f.resolve(e.interpolate(d[b],c)),e.setLocale(i)}else f.reject()};return g.displayName="fallbackTranslationResolver",W(a).then(g,f.reject),f.promise},Y=function(a,b,c,d){var e,f=t[a];if(f&&Object.prototype.hasOwnProperty.call(f,b)){if(d.setLocale(a),e=d.interpolate(f[b],c),"@:"===e.substr(0,2))return Y(a,e.substr(2),c,d);d.setLocale(i)}return e},Z=function(a,c){if(m){var d=b.get(m)(a,i,c);return void 0!==d?d:a}return a},$=function(a,b,c,e,f){var h=d.defer();if(a0?u:l,a,b,c,d)},ba=function(a,b,c){return _(u>0?u:l,a,b,c)},ca=function(a,b,c,e,f){var h=d.defer(),i=f?t[f]:t,j=c?H[c]:E;if(i&&Object.prototype.hasOwnProperty.call(i,a)){var k=i[a];"@:"===k.substr(0,2)?R(k.substr(2),b,c,e,f).then(h.resolve,h.reject):h.resolve(j.interpolate(k,b))}else{var l;m&&!F&&(l=Z(a,b)),f&&g&&g.length?aa(a,b,j,e).then(function(a){h.resolve(a)},function(a){h.reject(S(a))}):m&&!F&&l?e?h.resolve(e):h.resolve(l):e?h.resolve(e):h.reject(S(a))}return h.promise},da=function(a,b,c,d){var e,f=d?t[d]:t,h=E;if(H&&Object.prototype.hasOwnProperty.call(H,c)&&(h=H[c]),f&&Object.prototype.hasOwnProperty.call(f,a)){var i=f[a];e="@:"===i.substr(0,2)?da(i.substr(2),b,c,d):h.interpolate(i,b)}else{var j;m&&!F&&(j=Z(a,b)),d&&g&&g.length?(l=0,e=ba(a,b,h)):e=m&&!F&&j?j:S(a)}return e},ea=function(a){j===a&&(j=void 0),I[a]=void 0};R.preferredLanguage=function(a){return a&&O(a),e},R.cloakClassName=function(){return x},R.nestedObjectDelimeter=function(){return A},R.fallbackLanguage=function(a){if(void 0!==a&&null!==a){if(P(a),o&&g&&g.length)for(var b=0,c=g.length;c>b;b++)I[g[b]]||(I[g[b]]=U(g[b]));R.use(R.use())}return h?g[0]:g},R.useFallbackLanguage=function(a){if(void 0!==a&&null!==a)if(a){var b=J(g,a);b>-1&&(u=b)}else u=0},R.proposedLanguage=function(){return j},R.storage=function(){return f},R.negotiateLocale=L,R.use=function(a){if(!a)return i;var b=d.defer();c.$emit("$translateChangeStart",{language:a});var e=L(a);return e&&(a=e),!z&&t[a]||!o||I[a]?j===a&&I[a]?I[a].then(function(a){return b.resolve(a.key),a},function(a){return b.reject(a),d.reject(a)}):(b.resolve(a),T(a)):(j=a,I[a]=U(a).then(function(c){return M(c.key,c.table),b.resolve(c.key),j===a&&T(c.key),c},function(a){return c.$emit("$translateChangeError",{language:a}),b.reject(a),c.$emit("$translateChangeEnd",{language:a}),d.reject(a)}),I[a]["finally"](function(){ea(a)})),b.promise},R.storageKey=function(){return Q()},R.isPostCompilingEnabled=function(){return y},R.isForceAsyncReloadEnabled=function(){return z},R.refresh=function(a){function b(){f.resolve(),c.$emit("$translateRefreshEnd",{language:a})}function e(){f.reject(),c.$emit("$translateRefreshEnd",{language:a})}if(!o)throw new Error("Couldn't refresh translation table, no loader registered!");var f=d.defer();if(c.$emit("$translateRefreshStart",{language:a}),a)if(t[a]){var h=function(c){M(c.key,c.table),a===i&&T(i),b()};h.displayName="refreshPostProcessor",U(a).then(h,e)}else e();else{var j=[],k={};if(g&&g.length)for(var l=0,m=g.length;m>l;l++)j.push(U(g[l])),k[g[l]]=!0;i&&!k[i]&&j.push(U(i));var n=function(a){t={},angular.forEach(a,function(a){M(a.key,a.table)}),i&&T(i),b()};n.displayName="refreshPostProcessor",d.all(j).then(n,e)}return f.promise},R.instant=function(a,b,c,d){var f=d&&d!==i?L(d)||d:i;if(null===a||angular.isUndefined(a))return a;if(angular.isArray(a)){for(var h={},j=0,k=a.length;k>j;j++)h[a[j]]=R.instant(a[j],b,c,d);return h}if(angular.isString(a)&&a.length<1)return a;a&&(a=K.apply(a));var l,n=[];e&&n.push(e),f&&n.push(f),g&&g.length&&(n=n.concat(g));for(var o=0,p=n.length;p>o;o++){var s=n[o];if(t[s]&&"undefined"!=typeof t[s][a]&&(l=da(a,b,c,f)),"undefined"!=typeof l)break}return l||""===l||(q||r?l=S(a):(l=E.interpolate(a,b),m&&!F&&(l=Z(a,b)))),l},R.versionInfo=function(){return G},R.loaderCache=function(){return s},R.directivePriority=function(){return C},R.statefulFilter=function(){return D},R.isReady=function(){return B};var fa=d.defer();fa.promise.then(function(){B=!0}),R.onReady=function(a){var b=d.defer();return angular.isFunction(a)&&b.promise.then(a),B?b.resolve():fa.promise.then(b.resolve),b.promise};var ga=c.$on("$translateReady",function(){fa.resolve(),ga(),ga=null}),ha=c.$on("$translateChangeEnd",function(){fa.resolve(),ha(),ha=null});if(o){if(angular.equals(t,{})&&R.use()&&R.use(R.use()),g&&g.length)for(var ia=function(a){return M(a.key,a.table),c.$emit("$translateChangeEnd",{language:a.key}),a},ja=0,ka=g.length;ka>ja;ja++){var la=g[ja];(z||!t[la])&&(I[la]=U(la).then(ia))}}else c.$emit("$translateReady",{language:R.use()});return R}]}function d(a,b){"use strict";var c,d={},e="default";return d.setLocale=function(a){c=a},d.getInterpolationIdentifier=function(){return e},d.useSanitizeValueStrategy=function(a){return b.useStrategy(a),this},d.interpolate=function(c,d){d=d||{},d=b.sanitize(d,"params");var e=a(c)(d);return e=b.sanitize(e,"text")},d}function e(a,b,c,d,e,g){"use strict";var h=function(){return this.toString().replace(/^\s+|\s+$/g,"")};return{restrict:"AE",scope:!0,priority:a.directivePriority(),compile:function(b,i){var j=i.translateValues?i.translateValues:void 0,k=i.translateInterpolation?i.translateInterpolation:void 0,l=b[0].outerHTML.match(/translate-value-+/i),m="^(.*)("+c.startSymbol()+".*"+c.endSymbol()+")(.*)",n="^(.*)"+c.startSymbol()+"(.*)"+c.endSymbol()+"(.*)";return function(b,o,p){b.interpolateParams={},b.preText="",b.postText="",b.translateNamespace=f(b);var q={},r=function(a,c,d){if(c.translateValues&&angular.extend(a,e(c.translateValues)(b.$parent)),l)for(var f in d)if(Object.prototype.hasOwnProperty.call(c,f)&&"translateValue"===f.substr(0,14)&&"translateValues"!==f){var g=angular.lowercase(f.substr(14,1))+f.substr(15);a[g]=d[f]}},s=function(a){if(angular.isFunction(s._unwatchOld)&&(s._unwatchOld(),s._unwatchOld=void 0),angular.equals(a,"")||!angular.isDefined(a)){var d=h.apply(o.text()),e=d.match(m);if(angular.isArray(e)){b.preText=e[1],b.postText=e[3],q.translate=c(e[2])(b.$parent);var f=d.match(n);angular.isArray(f)&&f[2]&&f[2].length&&(s._unwatchOld=b.$watch(f[2],function(a){q.translate=a,y()}))}else q.translate=d?d:void 0}else q.translate=a;y()},t=function(a){p.$observe(a,function(b){q[a]=b,y()})};r(b.interpolateParams,p,i);var u=!0;p.$observe("translate",function(a){"undefined"==typeof a?s(""):""===a&&u||(q.translate=a,y()),u=!1});for(var v in p)p.hasOwnProperty(v)&&"translateAttr"===v.substr(0,13)&&t(v);if(p.$observe("translateDefault",function(a){b.defaultText=a,y()}),j&&p.$observe("translateValues",function(a){a&&b.$parent.$watch(function(){angular.extend(b.interpolateParams,e(a)(b.$parent))})}),l){var w=function(a){p.$observe(a,function(c){var d=angular.lowercase(a.substr(14,1))+a.substr(15);b.interpolateParams[d]=c})};for(var x in p)Object.prototype.hasOwnProperty.call(p,x)&&"translateValue"===x.substr(0,14)&&"translateValues"!==x&&w(x)}var y=function(){for(var a in q)q.hasOwnProperty(a)&&void 0!==q[a]&&z(a,q[a],b,b.interpolateParams,b.defaultText,b.translateNamespace)},z=function(b,c,d,e,f,g){c?(g&&"."===c.charAt(0)&&(c=g+c),a(c,e,k,f,d.translateLanguage).then(function(a){A(a,d,!0,b)},function(a){A(a,d,!1,b)})):A(c,d,!1,b)},A=function(b,c,e,f){if("translate"===f){e||"undefined"==typeof c.defaultText||(b=c.defaultText),o.empty().append(c.preText+b+c.postText);var g=a.isPostCompilingEnabled(),h="undefined"!=typeof i.translateCompile,j=h&&"false"!==i.translateCompile;(g&&!h||j)&&d(o.contents())(c)}else{e||"undefined"==typeof c.defaultText||(b=c.defaultText);var k=p.$attr[f];"data-"===k.substr(0,5)&&(k=k.substr(5)),k=k.substr(15),o.attr(k,b)}};(j||l||p.translateDefault)&&b.$watch("interpolateParams",y,!0),b.$watch("translateLanguage",y);var B=g.$on("$translateChangeSuccess",y);o.text().length?s(p.translate?p.translate:""):p.translate&&s(p.translate),y(),b.$on("$destroy",B)}}}}function f(a){"use strict";return a.translateNamespace?a.translateNamespace:a.$parent?f(a.$parent):void 0}function g(a,b){"use strict";return{compile:function(c){var d=function(){c.addClass(a.cloakClassName())},e=function(){c.removeClass(a.cloakClassName())};return a.onReady(function(){e()}),d(),function(c,f,g){g.translateCloak&&g.translateCloak.length&&(g.$observe("translateCloak",function(b){a(b).then(e,d)}),b.$on("$translateChangeSuccess",function(){a(g.translateCloak).then(e,d)}))}}}}function h(){"use strict";return{restrict:"A",scope:!0,compile:function(){return{pre:function(a,b,c){a.translateNamespace=f(a),a.translateNamespace&&"."===c.translateNamespace.charAt(0)?a.translateNamespace+=c.translateNamespace:a.translateNamespace=c.translateNamespace}}}}}function f(a){"use strict";return a.translateNamespace?a.translateNamespace:a.$parent?f(a.$parent):void 0}function i(){"use strict";return{restrict:"A",scope:!0,compile:function(){return function(a,b,c){c.$observe("translateLanguage",function(b){a.translateLanguage=b})}}}}function j(a,b){"use strict";var c=function(c,d,e,f){return angular.isObject(d)||(d=a(d)(this)),b.instant(c,d,e,f)};return b.statefulFilter()&&(c.$stateful=!0),c}function k(a){"use strict";return a("translations")}return angular.module("pascalprecht.translate",["ng"]).run(a),a.$inject=["$translate"],a.displayName="runTranslate",angular.module("pascalprecht.translate").provider("$translateSanitization",b),angular.module("pascalprecht.translate").constant("pascalprechtTranslateOverrider",{}).provider("$translate",c),c.$inject=["$STORAGE_KEY","$windowProvider","$translateSanitizationProvider","pascalprechtTranslateOverrider"],c.displayName="displayName",angular.module("pascalprecht.translate").factory("$translateDefaultInterpolation",d),d.$inject=["$interpolate","$translateSanitization"],d.displayName="$translateDefaultInterpolation",angular.module("pascalprecht.translate").constant("$STORAGE_KEY","NG_TRANSLATE_LANG_KEY"),angular.module("pascalprecht.translate").directive("translate",e),e.$inject=["$translate","$q","$interpolate","$compile","$parse","$rootScope"],e.displayName="translateDirective",angular.module("pascalprecht.translate").directive("translateCloak",g),g.$inject=["$translate","$rootScope"],g.displayName="translateCloakDirective",angular.module("pascalprecht.translate").directive("translateNamespace",h),h.displayName="translateNamespaceDirective",angular.module("pascalprecht.translate").directive("translateLanguage",i),i.displayName="translateLanguageDirective",angular.module("pascalprecht.translate").filter("translate",j),j.$inject=["$parse","$translate"],j.displayName="translateFilterFactory",angular.module("pascalprecht.translate").factory("$translationCache",k),k.$inject=["$cacheFactory"],k.displayName="$translationCache","pascalprecht.translate"}); -------------------------------------------------------------------------------- /lib/base64.js: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1999 Masanao Izumo 2 | * Version: 1.0 3 | * LastModified: Dec 25 1999 4 | * This library is free. You can redistribute it and/or modify it. 5 | */ 6 | 7 | /* 8 | * Interfaces: 9 | * b64 = base64encode(data); 10 | * data = base64decode(b64); 11 | */ 12 | 13 | (function() { 14 | 15 | var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 16 | var base64DecodeChars = new Array( 17 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 18 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 20 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, 21 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 22 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 23 | -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 24 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1); 25 | 26 | function base64encode(str) { 27 | var out, i, len; 28 | var c1, c2, c3; 29 | 30 | len = str.length; 31 | i = 0; 32 | out = ""; 33 | while(i < len) { 34 | c1 = str.charCodeAt(i++) & 0xff; 35 | if(i == len) 36 | { 37 | out += base64EncodeChars.charAt(c1 >> 2); 38 | out += base64EncodeChars.charAt((c1 & 0x3) << 4); 39 | out += "=="; 40 | break; 41 | } 42 | c2 = str.charCodeAt(i++); 43 | if(i == len) 44 | { 45 | out += base64EncodeChars.charAt(c1 >> 2); 46 | out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)); 47 | out += base64EncodeChars.charAt((c2 & 0xF) << 2); 48 | out += "="; 49 | break; 50 | } 51 | c3 = str.charCodeAt(i++); 52 | out += base64EncodeChars.charAt(c1 >> 2); 53 | out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)); 54 | out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)); 55 | out += base64EncodeChars.charAt(c3 & 0x3F); 56 | } 57 | return out; 58 | } 59 | 60 | function base64decode(str) { 61 | var c1, c2, c3, c4; 62 | var i, len, out; 63 | 64 | len = str.length; 65 | i = 0; 66 | out = ""; 67 | while(i < len) { 68 | /* c1 */ 69 | do { 70 | c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff]; 71 | } while(i < len && c1 == -1); 72 | if(c1 == -1) 73 | break; 74 | 75 | /* c2 */ 76 | do { 77 | c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff]; 78 | } while(i < len && c2 == -1); 79 | if(c2 == -1) 80 | break; 81 | 82 | out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4)); 83 | 84 | /* c3 */ 85 | do { 86 | c3 = str.charCodeAt(i++) & 0xff; 87 | if(c3 == 61) 88 | return out; 89 | c3 = base64DecodeChars[c3]; 90 | } while(i < len && c3 == -1); 91 | if(c3 == -1) 92 | break; 93 | 94 | out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2)); 95 | 96 | /* c4 */ 97 | do { 98 | c4 = str.charCodeAt(i++) & 0xff; 99 | if(c4 == 61) 100 | return out; 101 | c4 = base64DecodeChars[c4]; 102 | } while(i < len && c4 == -1); 103 | if(c4 == -1) 104 | break; 105 | out += String.fromCharCode(((c3 & 0x03) << 6) | c4); 106 | } 107 | return out; 108 | } 109 | 110 | if (!window.btoa) window.btoa = base64encode; 111 | if (!window.atob) window.atob = base64decode; 112 | 113 | })(); -------------------------------------------------------------------------------- /lib/canvas2image.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Canvas2Image v0.1 3 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk 4 | * MIT License [http://www.opensource.org/licenses/mit-license.php] 5 | */ 6 | 7 | var Canvas2Image = (function() { 8 | 9 | // check if we have canvas support 10 | var bHasCanvas = false; 11 | var oCanvas = document.createElement("canvas"); 12 | if (oCanvas.getContext("2d")) { 13 | bHasCanvas = true; 14 | } 15 | 16 | // no canvas, bail out. 17 | if (!bHasCanvas) { 18 | return { 19 | saveAsBMP : function(){}, 20 | saveAsPNG : function(){}, 21 | saveAsJPEG : function(){} 22 | } 23 | } 24 | 25 | var bHasImageData = !!(oCanvas.getContext("2d").getImageData); 26 | var bHasDataURL = !!(oCanvas.toDataURL); 27 | var bHasBase64 = !!(window.btoa); 28 | 29 | var strDownloadMime = "image/octet-stream"; 30 | 31 | // ok, we're good 32 | var readCanvasData = function(oCanvas) { 33 | var iWidth = parseInt(oCanvas.width); 34 | var iHeight = parseInt(oCanvas.height); 35 | return oCanvas.getContext("2d").getImageData(0,0,iWidth,iHeight); 36 | } 37 | 38 | // base64 encodes either a string or an array of charcodes 39 | var encodeData = function(data) { 40 | var strData = ""; 41 | if (typeof data == "string") { 42 | strData = data; 43 | } else { 44 | var aData = data; 45 | for (var i=0;i object containing the imagedata 156 | var makeImageObject = function(strSource) { 157 | var oImgElement = document.createElement("img"); 158 | oImgElement.src = strSource; 159 | return oImgElement; 160 | } 161 | 162 | var scaleCanvas = function(oCanvas, iWidth, iHeight) { 163 | if (iWidth && iHeight) { 164 | var oSaveCanvas = document.createElement("canvas"); 165 | oSaveCanvas.width = iWidth; 166 | oSaveCanvas.height = iHeight; 167 | oSaveCanvas.style.width = iWidth+"px"; 168 | oSaveCanvas.style.height = iHeight+"px"; 169 | 170 | var oSaveCtx = oSaveCanvas.getContext("2d"); 171 | 172 | oSaveCtx.drawImage(oCanvas, 0, 0, oCanvas.width, oCanvas.height, 0, 0, iWidth, iHeight); 173 | return oSaveCanvas; 174 | } 175 | return oCanvas; 176 | } 177 | 178 | return { 179 | 180 | saveAsPNG : function(oCanvas, bReturnImg, iWidth, iHeight) { 181 | if (!bHasDataURL) { 182 | return false; 183 | } 184 | var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight); 185 | var strData = oScaledCanvas.toDataURL("image/png"); 186 | if (bReturnImg) { 187 | return makeImageObject(strData); 188 | } else { 189 | saveFile(strData.replace("image/png", strDownloadMime)); 190 | } 191 | return true; 192 | }, 193 | 194 | saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) { 195 | if (!bHasDataURL) { 196 | return false; 197 | } 198 | 199 | var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight); 200 | var strMime = "image/jpeg"; 201 | var strData = oScaledCanvas.toDataURL(strMime); 202 | 203 | // check if browser actually supports jpeg by looking for the mime type in the data uri. 204 | // if not, return false 205 | if (strData.indexOf(strMime) != 5) { 206 | return false; 207 | } 208 | 209 | if (bReturnImg) { 210 | return makeImageObject(strData); 211 | } else { 212 | saveFile(strData.replace(strMime, strDownloadMime)); 213 | } 214 | return true; 215 | }, 216 | 217 | saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) { 218 | if (!(bHasImageData && bHasBase64)) { 219 | return false; 220 | } 221 | 222 | var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight); 223 | 224 | var oData = readCanvasData(oScaledCanvas); 225 | var strImgData = createBMP(oData); 226 | if (bReturnImg) { 227 | return makeImageObject(makeDataURI(strImgData, "image/bmp")); 228 | } else { 229 | saveFile(makeDataURI(strImgData, strDownloadMime)); 230 | } 231 | return true; 232 | } 233 | }; 234 | 235 | })(); -------------------------------------------------------------------------------- /lib/canvas2image.min.js: -------------------------------------------------------------------------------- 1 | var Canvas2Image=(function(){var bHasCanvas=false;var oCanvas=document.createElement("canvas");if(oCanvas.getContext("2d")){bHasCanvas=true}if(!bHasCanvas){return{saveAsBMP:function(){},saveAsPNG:function(){},saveAsJPEG:function(){}}}var bHasImageData=!!(oCanvas.getContext("2d").getImageData);var bHasDataURL=!!(oCanvas.toDataURL);var bHasBase64=!!(window.btoa);var strDownloadMime="image/octet-stream";var readCanvasData=function(oCanvas){var iWidth=parseInt(oCanvas.width);var iHeight=parseInt(oCanvas.height);return oCanvas.getContext("2d").getImageData(0,0,iWidth,iHeight)};var encodeData=function(data){var strData="";if(typeof data=="string"){strData=data}else{var aData=data;for(var i=0;i